@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="dailyfresh/settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="migrations" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.9 (dailyfresh-master)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
<option name="TEMPLATE_FOLDERS">
|
||||
<list>
|
||||
<option value="$MODULE_DIR$/templates" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="Django default" uuid="9064b16a-5590-4f62-8cff-ddf29d23a4a8">
|
||||
<driver-ref>mysql.8</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<imported>true</imported>
|
||||
<remarks>$PROJECT_DIR$/dailyfresh/settings.py</remarks>
|
||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mysql://127.0.0.1:3306/dailyfresh</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/static/页面说明.txt" charset="GBK" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,24 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="placeholder" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<value>
|
||||
<list size="1">
|
||||
<item index="0" class="java.lang.String" itemvalue="Django" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12 (online_store)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (dailyfresh-master)" project-jdk-type="Python SDK" />
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/dailyfresh-master.iml" filepath="$PROJECT_DIR$/.idea/dailyfresh-master.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CartConfig(AppConfig):
|
||||
name = 'apps.cart'
|
@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,11 @@
|
||||
from django.urls import path
|
||||
from apps.cart.views import CartAddView, CartInfoView, CartUpdateView, CartDeleteView
|
||||
|
||||
app_name = 'cart'
|
||||
|
||||
urlpatterns = [
|
||||
path('add', CartAddView.as_view(), name='add'), # 添加购物车记录
|
||||
path('', CartInfoView.as_view(), name='show'), # 显示购物车页面
|
||||
path('update', CartUpdateView.as_view(), name='update'), # 更新购物车记录
|
||||
path('delete', CartDeleteView.as_view(), name='delete'), # 删除购物车记录
|
||||
]
|
@ -0,0 +1,161 @@
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.views.generic.base import View
|
||||
from django_redis import get_redis_connection
|
||||
|
||||
from apps.goods.models import GoodsSKU
|
||||
from utils.mixin import LoginRequiredMixin
|
||||
|
||||
|
||||
# 购物车视图
|
||||
|
||||
class CartAddView(View):
|
||||
"""购物车记录添加"""
|
||||
|
||||
def post(self, request):
|
||||
"""处理购物车记录添加请求"""
|
||||
user = request.user
|
||||
if not user.is_authenticated:
|
||||
return JsonResponse({'res': 0, 'errmsg': '请先登录'})
|
||||
|
||||
# 接收数据
|
||||
sku_id = request.POST.get('sku_id')
|
||||
count = request.POST.get('count')
|
||||
|
||||
# 数据校验
|
||||
if not all([sku_id, count]):
|
||||
return JsonResponse({'res': 1, 'errmsg': '数据不完整'})
|
||||
|
||||
# 校验添加商品的数量
|
||||
try:
|
||||
count = int(count)
|
||||
except ValueError:
|
||||
return JsonResponse({'res': 2, 'errmsg': '商品数量出错'})
|
||||
|
||||
# 校验商品是否存在
|
||||
try:
|
||||
sku = GoodsSKU.objects.get(id=sku_id)
|
||||
except GoodsSKU.DoesNotExist:
|
||||
return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
|
||||
|
||||
# 业务处理:添加购物车记录
|
||||
conn = get_redis_connection('default')
|
||||
cart_key = f'cart_{user.id}'
|
||||
cart_count = conn.hget(cart_key, sku_id)
|
||||
if cart_count:
|
||||
count += int(cart_count)
|
||||
|
||||
# 校验商品的库存
|
||||
if count > sku.stock:
|
||||
return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
|
||||
|
||||
conn.hset(cart_key, sku_id, count)
|
||||
# 获取购物车商品条目数
|
||||
total_count = conn.hlen(cart_key)
|
||||
|
||||
# 返回应答
|
||||
return JsonResponse({'res': 5, 'message': '添加成功', 'total_count': total_count})
|
||||
|
||||
|
||||
class CartInfoView(LoginRequiredMixin, View):
|
||||
"""显示购物车页面"""
|
||||
|
||||
def get(self, request):
|
||||
"""处理显示购物车页面请求"""
|
||||
user = request.user
|
||||
|
||||
# 获取用户购物车中的商品信息
|
||||
conn = get_redis_connection('default')
|
||||
cart_key = f'cart_{user.id}'
|
||||
cart_dict = conn.hgetall(cart_key)
|
||||
|
||||
skus = []
|
||||
total_count = 0
|
||||
total_price = 0
|
||||
|
||||
# 遍历获取商品的信息
|
||||
for sku_id, count in cart_dict.items():
|
||||
sku = GoodsSKU.objects.get(id=sku_id)
|
||||
count = int(count)
|
||||
amount = sku.price * count
|
||||
sku.amount = amount
|
||||
sku.count = count
|
||||
skus.append(sku)
|
||||
|
||||
total_count += count
|
||||
total_price += amount
|
||||
|
||||
context = {
|
||||
'total_count': total_count,
|
||||
'total_price': total_price,
|
||||
'skus': skus,
|
||||
}
|
||||
|
||||
return render(request, 'cart.html', context)
|
||||
|
||||
|
||||
class CartUpdateView(View):
|
||||
"""购物车记录更新"""
|
||||
|
||||
def post(self, request):
|
||||
"""处理购物车记录更新请求"""
|
||||
user = request.user
|
||||
if not user.is_authenticated:
|
||||
return JsonResponse({'res': 0, 'errmsg': '请先登录'})
|
||||
|
||||
sku_id = request.POST.get('sku_id')
|
||||
count = request.POST.get('count')
|
||||
|
||||
if not all([sku_id, count]):
|
||||
return JsonResponse({'res': 1, 'errmsg': '数据不完整'})
|
||||
|
||||
try:
|
||||
count = int(count)
|
||||
except ValueError:
|
||||
return JsonResponse({'res': 2, 'errmsg': '商品数量出错'})
|
||||
|
||||
try:
|
||||
sku = GoodsSKU.objects.get(id=sku_id)
|
||||
except GoodsSKU.DoesNotExist:
|
||||
return JsonResponse({'res': 3, 'errmsg': '商品不存在'})
|
||||
|
||||
conn = get_redis_connection('default')
|
||||
cart_key = f'cart_{user.id}'
|
||||
|
||||
if count > sku.stock:
|
||||
return JsonResponse({'res': 4, 'errmsg': '商品库存不足'})
|
||||
|
||||
conn.hset(cart_key, sku_id, count)
|
||||
|
||||
total_count = sum(int(val) for val in conn.hvals(cart_key))
|
||||
|
||||
return JsonResponse({'res': 5, 'message': '更新成功', 'total_count': total_count})
|
||||
|
||||
|
||||
class CartDeleteView(View):
|
||||
"""购物车记录删除"""
|
||||
|
||||
def post(self, request):
|
||||
"""处理购物车记录删除请求"""
|
||||
user = request.user
|
||||
if not user.is_authenticated:
|
||||
return JsonResponse({'res': 0, 'errmsg': '请先登录'})
|
||||
|
||||
sku_id = request.POST.get('sku_id')
|
||||
|
||||
if not sku_id:
|
||||
return JsonResponse({'res': 1, 'errmsg': '数据不完整'})
|
||||
|
||||
try:
|
||||
sku = GoodsSKU.objects.get(id=sku_id)
|
||||
except GoodsSKU.DoesNotExist:
|
||||
return JsonResponse({'res': 2, 'errmsg': '商品不存在'})
|
||||
|
||||
conn = get_redis_connection('default')
|
||||
cart_key = f'cart_{user.id}'
|
||||
|
||||
conn.hdel(cart_key, sku_id)
|
||||
|
||||
total_count = sum(int(val) for val in conn.hvals(cart_key))
|
||||
|
||||
return JsonResponse({'res': 5, 'message': '删除成功', 'total_count': total_count})
|
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GoodsConfig(AppConfig):
|
||||
name = 'apps.goods'
|
@ -0,0 +1,150 @@
|
||||
# Generated by Django 2.2.3 on 2019-07-26 10:54
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import tinymce.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='GoodsSKU',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=20, verbose_name='商品名称')),
|
||||
('desc', models.CharField(max_length=256, verbose_name='商品简介')),
|
||||
('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='商品价格')),
|
||||
('unite', models.CharField(max_length=20, verbose_name='商品单位')),
|
||||
('image', models.ImageField(upload_to='goods', verbose_name='商品图片')),
|
||||
('stock', models.IntegerField(default=1, verbose_name='商品库存')),
|
||||
('sales', models.IntegerField(default=0, verbose_name='商品销量')),
|
||||
('status', models.SmallIntegerField(choices=[(0, '下线'), (1, '上线')], default=1, verbose_name='商品状态')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商品',
|
||||
'verbose_name_plural': '商品',
|
||||
'db_table': 'df_goods_sku',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GoodsSPU',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=20, verbose_name='商品SPU名称')),
|
||||
('detail', tinymce.models.HTMLField(blank=True, verbose_name='商品详情')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商品SPU',
|
||||
'verbose_name_plural': '商品SPU',
|
||||
'db_table': 'df_goods_spu',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GoodsType',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('name', models.CharField(max_length=20, verbose_name='种类名称')),
|
||||
('logo', models.CharField(max_length=20, verbose_name='标识')),
|
||||
('image', models.ImageField(upload_to='type', verbose_name='商品类型图片')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商品种类',
|
||||
'verbose_name_plural': '商品种类',
|
||||
'db_table': 'df_goods_type',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='IndexPromotionBanner',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('url', models.URLField(verbose_name='活动链接')),
|
||||
('name', models.CharField(max_length=20, verbose_name='活动名称')),
|
||||
('image', models.ImageField(upload_to='goods', verbose_name='图片路径')),
|
||||
('index', models.SmallIntegerField(default=0, verbose_name='展示顺序')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '主页促销活动',
|
||||
'verbose_name_plural': '主页促销活动',
|
||||
'db_table': 'df_index_promotion',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='IndexTypeGoodsBanner',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('display_type', models.SmallIntegerField(choices=[(0, '标题'), (1, '图片')], default=1, verbose_name='商品显示方式')),
|
||||
('index', models.SmallIntegerField(default=0, verbose_name='展示顺序')),
|
||||
('sku', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='goods.GoodsSKU', verbose_name='商品')),
|
||||
('type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='goods.GoodsType', verbose_name='商品类型')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '主页分类展示商品',
|
||||
'verbose_name_plural': '主页分类展示商品',
|
||||
'db_table': 'df_index_type_goods',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='IndexGoodsBanner',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('image', models.ImageField(upload_to='banner', verbose_name='图片')),
|
||||
('index', models.SmallIntegerField(default=0, verbose_name='展示顺序')),
|
||||
('sku', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='goods.GoodsSKU', verbose_name='商品')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '首页轮播图片',
|
||||
'verbose_name_plural': '首页轮播图片',
|
||||
'db_table': 'df_index_banner',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='goodssku',
|
||||
name='goods_spu',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='goods.GoodsSPU', verbose_name='商品SPU'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='goodssku',
|
||||
name='type',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='goods.GoodsType', verbose_name='商品种类'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='GoodsImage',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('image', models.ImageField(upload_to='goods', verbose_name='图片路径')),
|
||||
('sku', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='goods.GoodsSKU', verbose_name='商品')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '商品图片',
|
||||
'verbose_name_plural': '商品图片',
|
||||
'db_table': 'df_goods_image',
|
||||
},
|
||||
),
|
||||
]
|
@ -0,0 +1,125 @@
|
||||
from django.db import models
|
||||
from tinymce.models import HTMLField
|
||||
from db.base_model import BaseModel
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class GoodsType(BaseModel):
|
||||
"""商品类型模型类"""
|
||||
name = models.CharField(max_length=20, verbose_name='种类名称')
|
||||
logo = models.CharField(max_length=20, verbose_name='标识')
|
||||
image = models.ImageField(upload_to='type', verbose_name='商品类型图片')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_goods_type'
|
||||
verbose_name = '商品种类'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class GoodsSPU(BaseModel):
|
||||
"""商品SPU模型类"""
|
||||
name = models.CharField(max_length=20, verbose_name='商品SPU名称')
|
||||
detail = HTMLField(blank=True, verbose_name='商品详情')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_goods_spu'
|
||||
verbose_name = '商品SPU'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class GoodsSKU(BaseModel):
|
||||
"""商品SKU模型类"""
|
||||
STATUS_CHOICES = (
|
||||
(0, '下线'),
|
||||
(1, '上线'),
|
||||
)
|
||||
|
||||
type = models.ForeignKey('GoodsType', on_delete=models.CASCADE, verbose_name='商品种类')
|
||||
goods_spu = models.ForeignKey('GoodsSPU', on_delete=models.CASCADE, verbose_name='商品SPU')
|
||||
name = models.CharField(max_length=20, verbose_name='商品名称')
|
||||
desc = models.CharField(max_length=256, verbose_name='商品简介')
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品价格')
|
||||
unite = models.CharField(max_length=20, verbose_name='商品单位')
|
||||
image = models.ImageField(upload_to='goods', verbose_name='商品图片')
|
||||
stock = models.IntegerField(default=1, verbose_name='商品库存')
|
||||
sales = models.IntegerField(default=0, verbose_name='商品销量')
|
||||
status = models.SmallIntegerField(default=1, choices=STATUS_CHOICES, verbose_name='商品状态')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_goods_sku'
|
||||
verbose_name = '商品'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class GoodsImage(BaseModel):
|
||||
"""商品图片模型类"""
|
||||
sku = models.ForeignKey('GoodsSKU', on_delete=models.CASCADE, verbose_name='商品')
|
||||
image = models.ImageField(upload_to='goods', verbose_name='图片路径')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_goods_image'
|
||||
verbose_name = '商品图片'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.sku.name
|
||||
|
||||
|
||||
class IndexGoodsBanner(BaseModel):
|
||||
"""首页轮播商品展示模型类"""
|
||||
sku = models.ForeignKey('GoodsSKU', on_delete=models.CASCADE, verbose_name='商品')
|
||||
image = models.ImageField(upload_to='banner', verbose_name='图片')
|
||||
index = models.SmallIntegerField(default=0, verbose_name='展示顺序')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_index_banner'
|
||||
verbose_name = '首页轮播图片'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.sku.name
|
||||
|
||||
|
||||
class IndexTypeGoodsBanner(BaseModel):
|
||||
"""首页分类商品展示模型类"""
|
||||
DISPLAY_TYPE_CHOICES = (
|
||||
(0, '标题'),
|
||||
(1, '图片'),
|
||||
)
|
||||
type = models.ForeignKey('GoodsType', on_delete=models.CASCADE, verbose_name='商品类型')
|
||||
sku = models.ForeignKey('GoodsSKU', on_delete=models.CASCADE, verbose_name='商品')
|
||||
display_type = models.SmallIntegerField(default=1, choices=DISPLAY_TYPE_CHOICES, verbose_name='商品显示方式')
|
||||
index = models.SmallIntegerField(default=0, verbose_name='展示顺序')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_index_type_goods'
|
||||
verbose_name = '主页分类展示商品'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.sku.name
|
||||
|
||||
|
||||
class IndexPromotionBanner(BaseModel):
|
||||
"""首页促销活动模型类"""
|
||||
url = models.CharField(max_length=256, verbose_name='活动链接')
|
||||
name = models.CharField(max_length=20, verbose_name='活动名称')
|
||||
image = models.ImageField(upload_to='goods', verbose_name='图片路径')
|
||||
index = models.SmallIntegerField(default=0, verbose_name='展示顺序')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_index_promotion'
|
||||
verbose_name = '主页促销活动'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
@ -0,0 +1,19 @@
|
||||
# 定义索引类
|
||||
from haystack import indexes
|
||||
# 导入模型类
|
||||
from apps.goods.models import GoodsSKU
|
||||
# 指定对于某个类的某些数据建立索引
|
||||
# 索引类名格式:模型类名+Index
|
||||
|
||||
|
||||
|
||||
class GoodsSKUIndex(indexes.SearchIndex, indexes.Indexable):
|
||||
# 索引字段 指定根据表中哪些字段建立索引文件
|
||||
text = indexes.CharField(document=True, use_template=True)
|
||||
|
||||
def get_model(self):
|
||||
return GoodsSKU
|
||||
|
||||
# 建立索引的数据
|
||||
def index_queryset(self, using=None):
|
||||
return self.get_model().objects.all()
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,10 @@
|
||||
from django.urls import path, re_path
|
||||
from apps.goods.views import IndexView, DetailView, ListView
|
||||
|
||||
app_name = 'goods'
|
||||
|
||||
urlpatterns = [
|
||||
path('index/', IndexView.as_view(), name='index'), # Index view
|
||||
re_path(r'^goods/(?P<goods_id>\d+)$', DetailView.as_view(), name='detail'), # Detail view
|
||||
re_path(r'^goods/list/(?P<type_id>\d+)/(?P<page>\d+)$', ListView.as_view(), name='list'), # List view
|
||||
]
|
@ -0,0 +1,197 @@
|
||||
from django.core.paginator import Paginator
|
||||
from django.shortcuts import render, redirect, reverse
|
||||
from django.views.generic.base import View
|
||||
from django_redis import get_redis_connection
|
||||
from django.core.cache import cache
|
||||
from apps.goods.models import GoodsType, IndexGoodsBanner, IndexTypeGoodsBanner, IndexPromotionBanner, GoodsSKU
|
||||
from apps.order.models import OrderGoods
|
||||
|
||||
class IndexView(View):
|
||||
"""首页"""
|
||||
def get(self, request):
|
||||
"""首页"""
|
||||
# 获取用户信息
|
||||
user = request.user
|
||||
|
||||
# 尝试获取缓存数据
|
||||
context = cache.get('index_page_data')
|
||||
if not context:
|
||||
# 获取商品种类信息
|
||||
types = GoodsType.objects.all()
|
||||
|
||||
# 获取首页轮播商品信息
|
||||
goods_banners = IndexGoodsBanner.objects.all().order_by('index')
|
||||
|
||||
# 获取首页促销商品信息
|
||||
promotion_banners = IndexPromotionBanner.objects.all().order_by('index')
|
||||
|
||||
# 获取分类商品展示信息
|
||||
for type in types:
|
||||
image_goods_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1)
|
||||
font_goods_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=0)
|
||||
type.image_goods_banners = image_goods_banners
|
||||
type.font_goods_banners = font_goods_banners
|
||||
|
||||
# 组织上下文
|
||||
context = {
|
||||
'types': types,
|
||||
'goods_banners': goods_banners,
|
||||
'promotion_banners': promotion_banners,
|
||||
}
|
||||
# 设置缓存
|
||||
cache.set('index_page_data', context, 3600)
|
||||
|
||||
# 获取购物车中商品数量
|
||||
cart_count = self._get_cart_count(user)
|
||||
|
||||
context.update(user=user, cart_count=cart_count)
|
||||
|
||||
return render(request, 'index.html', context)
|
||||
|
||||
def _get_cart_count(self, user):
|
||||
"""获取用户购物车商品数量"""
|
||||
if user.is_authenticated:
|
||||
conn = get_redis_connection('default')
|
||||
cart_key = f'cart_{user.id}'
|
||||
return conn.hlen(cart_key)
|
||||
return 0
|
||||
|
||||
|
||||
class DetailView(View):
|
||||
"""详情页面"""
|
||||
def get(self, request, goods_id):
|
||||
"""显示详情页面"""
|
||||
# 获取商品SKU信息
|
||||
try:
|
||||
sku = GoodsSKU.objects.get(id=goods_id)
|
||||
except GoodsSKU.DoesNotExist:
|
||||
return redirect(reverse('goods:index'))
|
||||
|
||||
# 获取商品的分类信息
|
||||
types = GoodsType.objects.all()
|
||||
|
||||
# 获取商品的评论信息
|
||||
sku_orders = OrderGoods.objects.filter(sku=sku)
|
||||
|
||||
# 获取新商品信息
|
||||
new_skus = GoodsSKU.objects.filter(type=sku.type).order_by('-create_time')[:2]
|
||||
|
||||
# 获取同一个SPU的其他商品
|
||||
same_spu_skus = GoodsSKU.objects.filter(goods_spu=sku.goods_spu).exclude(id=goods_id)
|
||||
|
||||
# 获取用户购物车中的商品数目
|
||||
user = request.user
|
||||
cart_count = self._get_cart_count(user)
|
||||
|
||||
# 添加用户的历史浏览记录
|
||||
if user.is_authenticated:
|
||||
self._add_user_history(user, goods_id)
|
||||
|
||||
# 组织上下文
|
||||
context = {
|
||||
'sku': sku,
|
||||
'types': types,
|
||||
'sku_orders': sku_orders,
|
||||
'new_skus': new_skus,
|
||||
'same_spu_skus': same_spu_skus,
|
||||
'cart_count': cart_count,
|
||||
}
|
||||
|
||||
return render(request, 'detail.html', context)
|
||||
|
||||
def _get_cart_count(self, user):
|
||||
"""获取用户购物车商品数量"""
|
||||
if user.is_authenticated:
|
||||
conn = get_redis_connection('default')
|
||||
cart_key = f'cart_{user.id}'
|
||||
return conn.hlen(cart_key)
|
||||
return 0
|
||||
|
||||
def _add_user_history(self, user, goods_id):
|
||||
"""添加用户浏览历史记录"""
|
||||
conn = get_redis_connection('default')
|
||||
history_key = f'history_{user.id}'
|
||||
conn.lrem(history_key, 0, goods_id)
|
||||
conn.lpush(history_key, goods_id)
|
||||
conn.ltrim(history_key, 0, 4)
|
||||
|
||||
|
||||
class ListView(View):
|
||||
"""列表页"""
|
||||
def get(self, request, type_id, page):
|
||||
"""显示列表页"""
|
||||
# 获取种类信息
|
||||
try:
|
||||
type = GoodsType.objects.get(id=type_id)
|
||||
except GoodsType.DoesNotExist:
|
||||
return redirect(reverse('goods:index'))
|
||||
|
||||
# 获取排序方式
|
||||
sort = request.GET.get('sort', 'default')
|
||||
skus = self._get_skus_by_sort(type, sort)
|
||||
|
||||
# 对商品进行分页
|
||||
paginator = Paginator(skus, 1)
|
||||
page = self._get_valid_page(paginator, page)
|
||||
skus_page = paginator.page(page)
|
||||
pages = self._get_page_range(paginator, page)
|
||||
|
||||
# 获取商品的分类信息
|
||||
types = GoodsType.objects.all()
|
||||
|
||||
# 获取新商品信息
|
||||
new_skus = GoodsSKU.objects.filter(type=type).order_by('-create_time')[:2]
|
||||
|
||||
# 获取用户购物车中的商品数目
|
||||
user = request.user
|
||||
cart_count = self._get_cart_count(user)
|
||||
|
||||
# 组织上下文
|
||||
context = {
|
||||
'type': type,
|
||||
'skus_page': skus_page,
|
||||
'types': types,
|
||||
'new_skus': new_skus,
|
||||
'cart_count': cart_count,
|
||||
'sort': sort,
|
||||
'pages': pages,
|
||||
}
|
||||
|
||||
return render(request, 'list.html', context)
|
||||
|
||||
def _get_skus_by_sort(self, type, sort):
|
||||
"""获取排序后的商品SKU"""
|
||||
if sort == 'price':
|
||||
return GoodsSKU.objects.filter(type=type).order_by('price')
|
||||
elif sort == 'hot':
|
||||
return GoodsSKU.objects.filter(type=type).order_by('-sales')
|
||||
return GoodsSKU.objects.filter(type=type).order_by('-id')
|
||||
|
||||
def _get_valid_page(self, paginator, page):
|
||||
"""获取有效的分页页码"""
|
||||
try:
|
||||
page = int(page)
|
||||
except ValueError:
|
||||
page = 1
|
||||
if page > paginator.num_pages:
|
||||
page = 1
|
||||
return page
|
||||
|
||||
def _get_page_range(self, paginator, page):
|
||||
"""获取分页页码范围"""
|
||||
num_pages = paginator.num_pages
|
||||
if num_pages < 5:
|
||||
return range(1, num_pages + 1)
|
||||
if page <= 3:
|
||||
return range(1, 6)
|
||||
if num_pages - page <= 2:
|
||||
return range(num_pages - 4, num_pages + 1)
|
||||
return range(page - 2, page + 3)
|
||||
|
||||
def _get_cart_count(self, user):
|
||||
"""获取用户购物车商品数量"""
|
||||
if user.is_authenticated:
|
||||
conn = get_redis_connection('default')
|
||||
cart_key = f'cart_{user.id}'
|
||||
return conn.hlen(cart_key)
|
||||
return 0
|
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -0,0 +1,3 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjvgjL5EnRL1KrPqLFTZ0D4TyTFbSmmg/UWgkm1QyuqOYHZZ679beAsy6d2oY6WIcp5AqNrFIRiPz6jejRMNE9zhMEXQOG8zi/ttmJlyBlyW77FN5zrG5GPtGeZcr9cPEjc7Mh1srQLPHRYwDyyEHWp3VKe+tYCZMnPvJSch3iVCwY1jP9ApdozBCrLCs83m5p4mgoJLvrV0fp1sJKODDqazm8JixjOTZc5iZwI/ue7AGrFgxzSaU+pzhJiqBCoriq6w3hhzkGryGp4cXWNmjytn9rEK1wXOjaJPbWwtDtKtZiH4ou35pqCjTIWesqod+vEit5BUQuFoSzrM19LUx1QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAr3dngISSMTXZmWPosXgeJSTz/lSnNT/zXRXeCLSWfTSq6yML
|
||||
J9uuFxoiJJbHZdS1wFEnbqO8ZLX7Bqhm70/XITfrM8GDTjh8ITYvAnx60F/+7Fmk
|
||||
f8FTEt9KUW4DQQyf45EiIKW82CTClns5/d6hqt0Lxac/4jP1nhhL4Jde5DLggYdv
|
||||
sf2IlBOtqnhAcHOGFDq1ZBkp1uW457zjX0GujwbHKtI7YYf43aGtHwUtVBctyEXC
|
||||
NMJMpioSQQuFo4+SGp211lVIPTpzv++zCmL67R+yrXTGFqbqJpoJnrE93Fuy8Qlg
|
||||
HgHeTr75jic83+GwqOpN3KLy0Hpg595EoKx/PwIDAQABAoIBAA0rgipESRDGgPGh
|
||||
bRq88E5LasDhK7e0eBi5hnPS0iTNqjKB69lvBK8ZOAzVAFxlTcsEjFgilAZfHltO
|
||||
koNN09DbeJzm3msllDON9JNUMoenXOPyioVIRmr5NYPNJRNh1jJnd09KAVWb1Lsk
|
||||
vqKObkX712Fbf1EEI2BdZHyT//xSsV/xTZ5kXHJIIYzGuXtP5/h6m3s7qkAOOVzG
|
||||
LcFrCtGzifxZsprh8ztL0+PFvZywikGIg+GkBMaVA1Xz28WT9pggsa8KuY8dBI+K
|
||||
XRgsbKuVwccA3tIkgkgvdKKCH5b6Vbco8dxY4f1ZMt0Id3ouEVY2cq2vFmrGbFOP
|
||||
PIfucrkCgYEA43ZFFJDOon2yv2SydGlqXzKIEtJsRet0tpXu9pZFswt32vmIM/lX
|
||||
RHYFiq5ulFPZiYw3HFr6CwDM44eIn0/VJDh3/qMUptFhlQtg2WurIvNmZ7HqDK3S
|
||||
sXVr0jsFn1mD4t6PwMLNboCm6lfnQxvJXfHKaV/RW7RuVIcop6S/TFMCgYEAxXsd
|
||||
2DR+A0AI/JbXbRlVsUvV7B0/UH8Cz+PPJXsjazY0H6s+HQbXPJa0sG8mehCgXsDU
|
||||
wRz5n6gUy3ryBqzW/e8XTztZlHiRU+CXJNiqyfmzmfHGr+KKZvluFSWYdlQcu9qL
|
||||
43cqUqYddfwnEo6Q+/Hkdwi5BUq88APjJW6uw+UCgYA74zzG8GVnRN8WI0YU/lhC
|
||||
XkSTaBGXyyl8lTdId0I8pM1WuxJQVNrULJrC67Azn2wMGf28mntxADHxyhJ/l35P
|
||||
vgph4cAjN8eQfWFvfTieyCTzMlWkJvPtQzQzMtUFIoVl6yFAKEn8SSUpWCGMerlm
|
||||
4a1gVxkBIx1VZgyfLvIq/wKBgD+nyN35Rak8iekJolU7dmDZBhK+9rq2xixGzW3S
|
||||
fH9BkJmotDPdEaIpHgNFQMzV8Su50pqRAXHSVymj7sHyErb1y7ixc9Wk64ty+KVa
|
||||
5eqG/7qesaHeTyiUPES6wqNZx41SDAd9UPolK5fteJbFt7xOo4svF5y6E572UdCu
|
||||
Fc11AoGAAitTUwM3rDHjgutUL6/ffE56rgrkh50Up9vyR+gH2WO7AfIYgB3RRS08
|
||||
TQGnuS/CGCK8Q8tYS75W4wIrfdqVXAQbDf9skhIVUsVqKe9cC15nrmVZGTwToprt
|
||||
lHvbvCgxjoVti9tBv7mSDet8TqxqB7n+kkzk/cgzp5ht4i6xdMk=
|
||||
-----END RSA PRIVATE KEY-----
|
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class OrderConfig(AppConfig):
|
||||
name = 'apps.order'
|
@ -0,0 +1,51 @@
|
||||
# Generated by Django 2.2.3 on 2019-07-26 10:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='OrderGoods',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('count', models.IntegerField(default=1, verbose_name='商品数目')),
|
||||
('price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='商品价格')),
|
||||
('comment', models.CharField(max_length=256, verbose_name='评论')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '订单商品',
|
||||
'verbose_name_plural': '订单商品',
|
||||
'db_table': 'df_order_goods',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OrderInfo',
|
||||
fields=[
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('order_id', models.CharField(max_length=20, primary_key=True, serialize=False, verbose_name='订单编号')),
|
||||
('pay_method', models.SmallIntegerField(choices=[(1, '货到付款'), (2, '微信支付'), (3, '支付宝'), (4, '银联支付')], default=3, verbose_name='支付方式')),
|
||||
('total_count', models.IntegerField(default=1, verbose_name='商品数量')),
|
||||
('total_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='商品总价')),
|
||||
('transit_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='运费')),
|
||||
('order_status', models.SmallIntegerField(choices=[(1, '待支付'), (2, '待发货'), (3, '待收货'), (4, '待评价'), (5, '已完成')], default=1, verbose_name='订单状态')),
|
||||
('trade_no', models.CharField(max_length=20, verbose_name='支付编号')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '订单信息',
|
||||
'verbose_name_plural': '订单信息',
|
||||
'db_table': 'df_order_info',
|
||||
},
|
||||
),
|
||||
]
|
@ -0,0 +1,40 @@
|
||||
# Generated by Django 2.2.3 on 2019-07-26 10:54
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('goods', '0001_initial'),
|
||||
('order', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('user', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='orderinfo',
|
||||
name='address',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='user.Address', verbose_name='地址'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='orderinfo',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ordergoods',
|
||||
name='order',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='order.OrderInfo', verbose_name='订单'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ordergoods',
|
||||
name='sku',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='goods.GoodsSKU', verbose_name='商品SKU'),
|
||||
),
|
||||
]
|
@ -0,0 +1,65 @@
|
||||
from django.db import models
|
||||
from db.base_model import BaseModel
|
||||
|
||||
# Create your models here.
|
||||
|
||||
|
||||
class OrderInfo(BaseModel):
|
||||
"""订单信息模型类"""
|
||||
|
||||
PAY_METHOD = {
|
||||
'1': '货到付款',
|
||||
'2': '微信支付',
|
||||
'3': '支付宝',
|
||||
'4': '银联支付',
|
||||
}
|
||||
|
||||
PAY_METHOD_CHOICES = (
|
||||
(1, '货到付款'),
|
||||
(2, '微信支付'),
|
||||
(3, '支付宝'),
|
||||
(4, '银联支付'),
|
||||
)
|
||||
ORDER_STATUS = {
|
||||
'1': '待支付',
|
||||
'2': '待发货',
|
||||
'3': '待收货',
|
||||
'4': '待评价',
|
||||
'5': '已完成',
|
||||
}
|
||||
|
||||
ORDER_STATUS_CHOICES = (
|
||||
(1, '待支付'),
|
||||
(2, '待发货'),
|
||||
(3, '待收货'),
|
||||
(4, '待评价'),
|
||||
(5, '已完成'),
|
||||
)
|
||||
|
||||
order_id = models.CharField(max_length=20, primary_key=True, verbose_name='订单编号')
|
||||
user = models.ForeignKey('user.User', on_delete=models.CASCADE, verbose_name='用户')
|
||||
address = models.ForeignKey('user.Address', on_delete=models.CASCADE, verbose_name='地址')
|
||||
pay_method = models.SmallIntegerField(default=3, choices=PAY_METHOD_CHOICES, verbose_name='支付方式')
|
||||
total_count = models.IntegerField(default=1, verbose_name='商品数量')
|
||||
total_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品总价')
|
||||
transit_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='运费')
|
||||
order_status = models.SmallIntegerField(default=1, choices=ORDER_STATUS_CHOICES, verbose_name='订单状态')
|
||||
trade_no = models.CharField(max_length=20, default='', verbose_name='支付编号')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_order_info'
|
||||
verbose_name = '订单信息'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
class OrderGoods(BaseModel):
|
||||
"""订单商品模型类"""
|
||||
order = models.ForeignKey('OrderInfo', on_delete=models.CASCADE, verbose_name='订单')
|
||||
sku = models.ForeignKey('goods.GoodsSKU', on_delete=models.CASCADE, verbose_name='商品SKU')
|
||||
count = models.IntegerField(default=1, verbose_name='商品数目')
|
||||
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='商品价格')
|
||||
comment = models.CharField(max_length=256, default='', verbose_name='评论')
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_order_goods'
|
||||
verbose_name = '订单商品'
|
||||
verbose_name_plural = verbose_name
|
@ -0,0 +1,12 @@
|
||||
from django.urls import path, re_path
|
||||
from apps.order.views import OrderPlaceView, OrderCommitView, OrderPayView, CheckPayView, CommentView
|
||||
|
||||
app_name = 'order'
|
||||
|
||||
urlpatterns = [
|
||||
path('place', OrderPlaceView.as_view(), name='place'), # 提交订单页面显示
|
||||
path('commit', OrderCommitView.as_view(), name='commit'), # 订单创建
|
||||
path('pay', OrderPayView.as_view(), name='pay'), # 订单支付
|
||||
path('check', CheckPayView.as_view(), name='check'), # 查看订单支付状态
|
||||
re_path(r'^comment/(?P<order_id>.+)$', CommentView.as_view(), name='comment'), # 订单评论
|
||||
]
|
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UserConfig(AppConfig):
|
||||
name = 'apps.user'
|
@ -0,0 +1,69 @@
|
||||
# Generated by Django 2.2.3 on 2019-07-26 10:54
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0011_update_proxy_permissions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户',
|
||||
'verbose_name_plural': '用户',
|
||||
'db_table': 'df_user',
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Address',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
|
||||
('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
|
||||
('receiver', models.CharField(max_length=20, verbose_name='收件人')),
|
||||
('addr', models.CharField(max_length=256, verbose_name='收货地址')),
|
||||
('zip_code', models.CharField(max_length=6, null=True, verbose_name='邮政编码')),
|
||||
('phone', models.CharField(max_length=11, verbose_name='联系电话')),
|
||||
('is_default', models.BooleanField(default=False, verbose_name='是否默认')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='所属用户')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '地址',
|
||||
'verbose_name_plural': '地址',
|
||||
'db_table': 'df_address',
|
||||
},
|
||||
),
|
||||
]
|
@ -0,0 +1,49 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from itsdangerous import URLSafeTimedSerializer as Serializer
|
||||
from db.base_model import BaseModel
|
||||
from django.db import models
|
||||
|
||||
# AddressManager Class
|
||||
class AddressManager(models.Manager):
|
||||
"""地址模型管理类"""
|
||||
def get_default_address(self, user):
|
||||
"""获取用户的默认地址"""
|
||||
try:
|
||||
address = self.get(user=user, is_default=True)
|
||||
except self.model.DoesNotExist:
|
||||
address = None
|
||||
return address
|
||||
|
||||
# User Model
|
||||
class User(AbstractUser, BaseModel):
|
||||
"""用户模型类"""
|
||||
def generate_active_token(self):
|
||||
"""生成用户签名字符串"""
|
||||
serializer = Serializer(settings.SECRET_KEY, 3600)
|
||||
info = {'confirm': self.id}
|
||||
token = serializer.dumps(info)
|
||||
return token.decode()
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_user'
|
||||
verbose_name = '用户'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# Address Model
|
||||
class Address(BaseModel):
|
||||
"""地址模型类"""
|
||||
user = models.ForeignKey('User', on_delete=models.CASCADE, verbose_name='所属用户')
|
||||
receiver = models.CharField(max_length=20, verbose_name='收件人')
|
||||
addr = models.CharField(max_length=256, verbose_name='收货地址')
|
||||
zip_code = models.CharField(max_length=6, null=True, verbose_name='邮政编码')
|
||||
phone = models.CharField(max_length=11, verbose_name='联系电话')
|
||||
is_default = models.BooleanField(default=False, verbose_name='是否默认')
|
||||
|
||||
# 自定义一个模型管理器对象
|
||||
objects = AddressManager()
|
||||
|
||||
class Meta:
|
||||
db_table = 'df_address'
|
||||
verbose_name = '地址'
|
||||
verbose_name_plural = verbose_name
|
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
@ -0,0 +1,19 @@
|
||||
from django.urls import path, re_path
|
||||
from apps.user.views import (
|
||||
RegisterView, ActiveView, LoginView, UserInfoView,
|
||||
UserOrderView, UserSiteView, LogoutView
|
||||
)
|
||||
from apps.user import views
|
||||
|
||||
app_name = 'user'
|
||||
|
||||
urlpatterns = [
|
||||
path('index/', views.index, name='index'),
|
||||
path('register/', RegisterView.as_view(), name='register'), # Register
|
||||
re_path(r'active/(?P<token>.*)/$', ActiveView.as_view(), name='active'), # User Activation
|
||||
path('login/', LoginView.as_view(), name='login'), # Login
|
||||
path('logout/', LogoutView.as_view(), name='logout'), # Logout
|
||||
path('', UserInfoView.as_view(), name='user'), # User Info
|
||||
re_path(r'^order/(?P<page>\d+)/$', UserOrderView.as_view(), name='order'), # Orders
|
||||
path('address/', UserSiteView.as_view(), name='address'), # Address
|
||||
]
|
@ -0,0 +1,215 @@
|
||||
import re
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.core.paginator import Paginator
|
||||
from django.shortcuts import render, redirect, reverse, HttpResponse
|
||||
from django.views.generic import View
|
||||
from itsdangerous import URLSafeTimedSerializer as Serializer, SignatureExpired
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
from celery_tasks.tasks import send_register_active_email
|
||||
from utils.mixin import LoginRequiredMixin
|
||||
from django_redis import get_redis_connection
|
||||
from apps.goods.models import GoodsSKU
|
||||
from apps.order.models import OrderInfo, OrderGoods
|
||||
from apps.user.models import User, Address, AddressManager
|
||||
|
||||
|
||||
# Index View
|
||||
def index(request):
|
||||
return render(request, 'index.html')
|
||||
|
||||
|
||||
# Function-based Register View (Commented out)
|
||||
# def register(request):
|
||||
# """显示注册页面"""
|
||||
# if request.method == 'GET':
|
||||
# return render(request, 'register.html')
|
||||
# elif request.method == 'POST':
|
||||
# # Handle registration logic
|
||||
# pass
|
||||
|
||||
# Class-based Register View
|
||||
class RegisterView(View):
|
||||
"""注册"""
|
||||
|
||||
def get(self, request):
|
||||
return render(request, 'register.html')
|
||||
|
||||
def post(self, request):
|
||||
# 接受数据
|
||||
username = request.POST.get('user_name')
|
||||
password = request.POST.get('pwd')
|
||||
email = request.POST.get('email')
|
||||
allow = request.POST.get('allow')
|
||||
|
||||
# 数据处理
|
||||
if not all([username, password, email]):
|
||||
return render(request, 'register.html', {'errmsg': '数据不完整'})
|
||||
|
||||
if not re.match(r'^[a-z0-9][\w.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
|
||||
return render(request, 'register.html', {'errmsg': '邮箱格式不正确'})
|
||||
|
||||
if allow != 'on':
|
||||
return render(request, 'register.html', {'errmsg': '请同意协议'})
|
||||
|
||||
# 校验用户名是否重复
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
user = None
|
||||
|
||||
if user:
|
||||
return render(request, 'register.html', {'errmsg': '用户名已存在'})
|
||||
|
||||
# 进行用户注册
|
||||
user = User.objects.create_user(username, email, password)
|
||||
user.is_active = 0
|
||||
user.is_superuser = False
|
||||
user.save()
|
||||
|
||||
# 发送激活邮件,包含激活链接
|
||||
serializer = Serializer(settings.SECRET_KEY, 3600)
|
||||
info = {'confirm': user.id}
|
||||
token = serializer.dumps(info).decode()
|
||||
|
||||
# 为处理者分配任务
|
||||
send_register_active_email.delay(email, username, token)
|
||||
|
||||
return redirect(reverse('goods:index'))
|
||||
|
||||
|
||||
# User Activation View
|
||||
class ActiveView(View):
|
||||
"""用户激活"""
|
||||
|
||||
def get(self, request, token):
|
||||
try:
|
||||
serializer = Serializer(settings.SECRET_KEY, 3600)
|
||||
token = token.encode()
|
||||
info = serializer.loads(token)
|
||||
user_id = info['confirm']
|
||||
user = User.objects.get(id=user_id)
|
||||
user.is_active = 1
|
||||
user.save()
|
||||
return redirect(reverse('user:login'))
|
||||
except SignatureExpired:
|
||||
return HttpResponse('激活链接已过期')
|
||||
|
||||
|
||||
# Login View
|
||||
class LoginView(View):
|
||||
"""登录"""
|
||||
|
||||
def get(self, request):
|
||||
if 'username' in request.COOKIES:
|
||||
username = request.COOKIES.get('username')
|
||||
checked = 'checked'
|
||||
else:
|
||||
username = ''
|
||||
checked = ''
|
||||
context = {'username': username, 'checked': checked}
|
||||
return render(request, 'login.html', context)
|
||||
|
||||
def post(self, request):
|
||||
username = request.POST.get('username')
|
||||
password = request.POST.get('pwd')
|
||||
if not all([username, password]):
|
||||
return render(request, 'login.html', {'errmsg': '数据不完整'})
|
||||
user = authenticate(username=username, password=password)
|
||||
if user:
|
||||
if user.is_active:
|
||||
login(request, user)
|
||||
next_url = request.GET.get('next', reverse('goods:index'))
|
||||
response = redirect(next_url)
|
||||
remember = request.POST.get('remember')
|
||||
if remember == 'on':
|
||||
response.set_cookie('username', username, max_age=7 * 24 * 3600)
|
||||
else:
|
||||
response.delete_cookie('username')
|
||||
return response
|
||||
else:
|
||||
return render(request, 'login.html', {'errmsg': '账户未激活'})
|
||||
else:
|
||||
return render(request, 'login.html', {'errmsg': '用户名或密码错误'})
|
||||
|
||||
|
||||
# Logout View
|
||||
class LogoutView(View):
|
||||
"""退出登录"""
|
||||
|
||||
def get(self, request):
|
||||
logout(request)
|
||||
return redirect(reverse('goods:index'))
|
||||
|
||||
|
||||
# User Info View
|
||||
class UserInfoView(LoginRequiredMixin, View):
|
||||
"""用户中心——信息页"""
|
||||
|
||||
def get(self, request):
|
||||
user = request.user
|
||||
address = Address.objects.get_default_address(user)
|
||||
con = get_redis_connection('default')
|
||||
history_key = f'history_{user.id}'
|
||||
sku_ids = con.lrange(history_key, 0, 4)
|
||||
goods_list = [GoodsSKU.objects.get(id=i) for i in sku_ids]
|
||||
context = {'page': 'user', 'user': user, 'address': address, 'goods_list': goods_list}
|
||||
return render(request, 'user_center_info.html', context)
|
||||
|
||||
|
||||
# User Order View
|
||||
class UserOrderView(LoginRequiredMixin, View):
|
||||
"""用户中心——订单页"""
|
||||
|
||||
def get(self, request, page):
|
||||
user = request.user
|
||||
orders = OrderInfo.objects.filter(user=user)
|
||||
for order in orders:
|
||||
order_skus = OrderGoods.objects.filter(order_id=order.order_id)
|
||||
for order_sku in order_skus:
|
||||
order_sku.amount = order_sku.price * order_sku.count
|
||||
order.order_skus = order_skus
|
||||
order.status_name = OrderInfo.ORDER_STATUS[str(order.order_status)]
|
||||
paginator = Paginator(orders, 1)
|
||||
try:
|
||||
page = int(page)
|
||||
except Exception:
|
||||
page = 1
|
||||
order_page = paginator.page(page)
|
||||
num_pages = paginator.num_pages
|
||||
if num_pages < 5:
|
||||
pages = range(1, num_pages + 1)
|
||||
elif page <= 3:
|
||||
pages = range(1, 6)
|
||||
elif num_pages - page <= 2:
|
||||
pages = range(num_pages - 4, num_pages + 1)
|
||||
else:
|
||||
pages = range(page - 2, page + 3)
|
||||
context = {'order_page': order_page, 'pages': pages, 'page': 'order'}
|
||||
return render(request, 'user_center_order.html', context)
|
||||
|
||||
|
||||
# User Address View
|
||||
class UserSiteView(LoginRequiredMixin, View):
|
||||
"""用户中心——地址页"""
|
||||
|
||||
def get(self, request):
|
||||
user = request.user
|
||||
address = Address.objects.get_default_address(user)
|
||||
return render(request, 'user_center_site.html', {'address': address, 'user': user})
|
||||
|
||||
def post(self, request):
|
||||
receiver = request.POST.get('receiver')
|
||||
addr = request.POST.get('addr')
|
||||
zip_code = request.POST.get('zip_code')
|
||||
phone = request.POST.get('phone')
|
||||
if not all([receiver, addr, phone]):
|
||||
return render(request, 'user_center_site.html', {'errmsg': '数据不完整'})
|
||||
if not re.match(r'^1[3|4|5|7|8][0-9]{9}$', phone):
|
||||
return render(request, 'user_center_site.html', {'errmsg': '手机格式错误'})
|
||||
user = request.user
|
||||
address = Address.objects.get_default_address(user)
|
||||
is_default = not bool(address)
|
||||
Address.objects.create(user=user, receiver=receiver, addr=addr, zip_code=zip_code, phone=phone,
|
||||
is_default=is_default)
|
||||
return redirect(reverse('user:address'))
|
@ -0,0 +1,73 @@
|
||||
# 使用celery
|
||||
|
||||
# # 在任务一段加这几句代码
|
||||
# import os
|
||||
# import django
|
||||
# os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dailyfresh.settings')
|
||||
# django.setup()
|
||||
import os
|
||||
import time
|
||||
|
||||
from django.template import loader
|
||||
|
||||
from dailyfresh import settings
|
||||
from celery import Celery
|
||||
from django_redis import get_redis_connection
|
||||
|
||||
from apps.goods.models import GoodsType, IndexGoodsBanner, IndexTypeGoodsBanner, IndexPromotionBanner
|
||||
# 创建一个Celery类的实例对象
|
||||
from django.core.mail import send_mail
|
||||
app = Celery('celery_tasks.tasks', broker='redis://192.168.209.130:6379/8')
|
||||
|
||||
# 定义任务函数
|
||||
@app.task
|
||||
def send_register_active_email(to_email, username, token):
|
||||
"""发送激活邮件"""
|
||||
# 发送邮件
|
||||
subject = '天天生鲜欢迎信息'
|
||||
message = ''
|
||||
sender = settings.EMAIL_FROM
|
||||
receiver = [to_email]
|
||||
html_message = '<h1>{0}, 欢迎您成为天天生鲜注册会员</h1>请点击下面链接激活您的账户<br/><a href="http://127.0.0.1:8000/user/active/{1}">http://127.0.0.1/user/active/{2}</a>'.format(
|
||||
username, token, token)
|
||||
send_mail(subject, message, sender, receiver, html_message=html_message)
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
@app.task
|
||||
def generate_static_index_html():
|
||||
"""产生首页静态页面"""
|
||||
|
||||
# 获取商品种类信息
|
||||
types = GoodsType.objects.all()
|
||||
|
||||
# 获取首页轮播商品信息
|
||||
goods_banners = IndexGoodsBanner.objects.all().order_by('index')
|
||||
|
||||
# 获取首页促销商品信息
|
||||
promotion_banners = IndexPromotionBanner.objects.all().order_by('index')
|
||||
|
||||
# 获取分类商品展示信息
|
||||
for type in types:
|
||||
image_goods_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=1)
|
||||
font_goods_banners = IndexTypeGoodsBanner.objects.filter(type=type, display_type=0)
|
||||
type.image_goods_banners = image_goods_banners
|
||||
type.font_goods_banners = font_goods_banners
|
||||
|
||||
# 组织上下文
|
||||
context = {
|
||||
'types': types,
|
||||
'goods_banners': goods_banners,
|
||||
'promotion_goods': promotion_banners,
|
||||
}
|
||||
|
||||
# s使用模板
|
||||
# 1. 加载模板文件,返回模板对象
|
||||
temp = loader.get_template('static_index.html')
|
||||
# 2. 渲染模板
|
||||
static_index_html = temp.render(context)
|
||||
|
||||
# 生成对应静态文件
|
||||
save_path = os.path.join(settings.BASE_DIR, 'static/index.html')
|
||||
with open(save_path, 'w') as f:
|
||||
f.write(static_index_html)
|
@ -0,0 +1,202 @@
|
||||
"""
|
||||
Django settings for dailyfresh project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 2.2.3.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/2.2/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'qi1pgrrshxdstv3v71b8ax%@2p)zh#&_hy5mo633xh3$+g-bs('
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'tinymce', # 富文本编辑器
|
||||
'haystack', # 全文检索框架
|
||||
# sys.path.insert(0, os.path.join(BASE_DIR, 'apps')
|
||||
'apps.goods', # 商品模块
|
||||
'apps.cart', # 购物车模块
|
||||
'apps.order', # 订单模块
|
||||
'apps.user', # 用户模块
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'dailyfresh.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'dailyfresh.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'dailyfresh',
|
||||
'USER': 'root',
|
||||
'PASSWORD': '1234567',
|
||||
'HOST': '127.0.0.1',
|
||||
'POST': '3306',
|
||||
}
|
||||
}
|
||||
|
||||
# django认证系统使用的模型类
|
||||
AUTH_USER_MODEL='user.User'
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/2.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
|
||||
|
||||
# 富文本编辑器配置
|
||||
TINYMCE_DEFAULT_CONFIG = {
|
||||
'theme': 'advanced',
|
||||
'width': 600,
|
||||
'height': 400,
|
||||
}
|
||||
|
||||
# 发送邮件配置
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
|
||||
# EMAIL_USE_TLS = False
|
||||
# smpt服务地址
|
||||
EMAIL_HOST = 'smtp.163.com'
|
||||
EMAIL_PORT = 25
|
||||
# 邮箱
|
||||
EMAIL_HOST_USER = 'a1691795341@163.com'
|
||||
# 授权密码
|
||||
EMAIL_HOST_PASSWORD = 'w809326582'
|
||||
# DEFAULT_FROM_EMAIL = 'w1691795341@163.com'
|
||||
EMAIL_FROM = '天天生鲜<a1691795341@163.com>'
|
||||
|
||||
# Django的缓存配置
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django_redis.cache.RedisCache',
|
||||
'LOCATION': 'redis://192.168.209.130:6379/1',
|
||||
'OPTIONS': {
|
||||
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# 配置SESSION的存储
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
||||
SESSION_CACHE_ALIAS = "default"
|
||||
|
||||
# 配置登录url地址
|
||||
LOGIN_URL = '/user/login'
|
||||
|
||||
# 设置django的默认文件存储类
|
||||
DEFAULT_FILE_STORAGE = 'utils.fdfs.storage.FDFSStorage'
|
||||
|
||||
# 设置fdfs使用的client.conf的文件路径
|
||||
FDFS_CLIENT_CONF = '/etc/fdfs/client.conf'
|
||||
|
||||
# 设置fdfs存储服务器上Nginx的ip和port
|
||||
FDFS_URL = 'http://192.168.209.130:8888/'
|
||||
|
||||
# 全文检索框架配置
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
'default': {
|
||||
# 使用whoosh
|
||||
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
|
||||
# 索引文件路径
|
||||
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
|
||||
}
|
||||
}
|
||||
|
||||
# 当添加、修改、删除数据时,自动生成索引
|
||||
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
|
||||
# 指定搜索每页显示条数 默认20
|
||||
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 1
|
@ -0,0 +1,37 @@
|
||||
"""dailyfresh URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.urls import path
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from apps.user import views
|
||||
from apps.user.views import RegisterView, ActiveView, LoginView, UserInfoView, UserOrderView, UserSiteView, LogoutView
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.index, name='index'), # 根路径指向index视图
|
||||
# path('index/', views.index, name='index'),
|
||||
path('admin/', admin.site.urls),
|
||||
# path('login/', views.login_view, name='login'),
|
||||
# path('register/', views.register_view, name='register'),
|
||||
path('login/', LoginView.as_view(), name='login'), # 登录
|
||||
path('logout/', LogoutView.as_view(), name='logout'), # 退出登录
|
||||
path('register/', RegisterView.as_view(), name='register'), # 注册
|
||||
path('tinymce/', include('tinymce.urls')), # 富文本编辑器
|
||||
path('search/', include('haystack.urls')), # 全文检索
|
||||
path('user/', include(('apps.user.urls', 'user'), namespace='user')), # 用户模块
|
||||
path('cart/', include(('apps.cart.urls', 'cart'), namespace='cart')), # 购物车模块
|
||||
path('order/', include(('apps.order.urls', 'order'), namespace='order')), # 订单模块
|
||||
path('goods/', include(('apps.goods.urls', 'goods'), namespace='goods')), # 商品模块
|
||||
]
|
@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for dailyfresh project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dailyfresh.settings')
|
||||
|
||||
application = get_wsgi_application()
|
@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
|
||||
class BaseModel(models.Model):
|
||||
'''模型抽象基类'''
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||||
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||||
is_delete = models.BooleanField(default=False, verbose_name='删除标记')
|
||||
|
||||
class Meta:
|
||||
# 说明是一个抽象基类
|
||||
abstract = True
|
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dailyfresh.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
如果 安装有其他fdfs包, 要先卸载完再安装py3Fdfs
|
||||
|
||||
pip install py3Fdfs
|
||||
测试
|
||||
|
||||
from fdfs_client.client import *
|
||||
client_conf_obj = get_tracker_conf('/etc/fdfs/client.conf')
|
||||
client = Fdfs_client(client_conf_obj)
|
||||
ret = client.upload_by_filename('test')
|
||||
ret
|
||||
|
||||
{'Group name': b'group1', 'Remote file_id': b'group1/M00/00/00/wKjRgl09sfWALPKYAAAAAAAAAAA765.txt', 'Status': 'Upload successed.', 'Local file name': 'test.txt', 'Uploaded size': '0B', 'Storage IP': b'192.168.209.130'}
|
||||
|
||||
|
||||
关于storege.conf配置文件
|
||||
|
||||
tracker_server = 填写所在服务器的外网ip
|
||||
|
||||
安全组开放 配置文件里的23000端口
|
@ -0,0 +1,215 @@
|
||||
|
||||
本文标题: Django中redis的使用方法(包括安装、配置、启动)
|
||||
本文地址: http://www.cppcns.com/shujuku/redis/220910.html
|
||||
|
||||
|
||||
./redis-4.0.8/src/redis-cli [-h] [-p]
|
||||
127.0.0.1:6379> ping
|
||||
PONG
|
||||
127.0.0.1:6379> select 5
|
||||
OK
|
||||
127.0.0.1:6379[5]> keys *
|
||||
(empty list or set)
|
||||
127.0.0.1:6379[5]> set name itchat
|
||||
OK
|
||||
127.0.0.1:6379[5]> get name
|
||||
"itchat"
|
||||
127.0.0.1:6379[5]> set name itchat2
|
||||
OK
|
||||
127.0.0.1:6379[5]> get name
|
||||
"itchat2"
|
||||
127.0.0.1:6379[5]> keys *
|
||||
1) "name"
|
||||
127.0.0.1:6379[5]> setex aa 3 ii
|
||||
OK
|
||||
127.0.0.1:6379[5]> keys *
|
||||
1) "name"
|
||||
127.0.0.1:6379[5]> setex aa 3 ii
|
||||
OK
|
||||
127.0.0.1:6379[5]> get aa
|
||||
"ii"
|
||||
127.0.0.1:6379[5]> get aa
|
||||
(nil)
|
||||
127.0.0.1:6379[5]> mset a1 java a2 python a3 c
|
||||
OK
|
||||
127.0.0.1:6379[5]> get a1
|
||||
"java"
|
||||
127.0.0.1:6379[5]> get a2
|
||||
"python"
|
||||
127.0.0.1:6379[5]> get a3
|
||||
"c"
|
||||
127.0.0.1:6379[5]> append a1 haha
|
||||
(integer) 8
|
||||
127.0.0.1:6379[5]> get a1
|
||||
"javahaha"
|
||||
127.0.0.1:6379[5]> exists a
|
||||
(integer) 0
|
||||
127.0.0.1:6379[5]> exists a2
|
||||
(integer) 1
|
||||
127.0.0.1:6379[5]> type name
|
||||
string
|
||||
127.0.0.1:6379[5]> mget a1 a2 a3
|
||||
1) "javahaha"
|
||||
2) "python"
|
||||
3) "c"
|
||||
127.0.0.1:6379[5]> del a1 a2 a3
|
||||
(integer) 3
|
||||
127.0.0.1:6379[5]> keys *
|
||||
1) "name"
|
||||
127.0.0.1:6379[5]> set a1 python
|
||||
OK
|
||||
127.0.0.1:6379[5]> expire a1 3
|
||||
(integer) 1
|
||||
127.0.0.1:6379[5]> get a1
|
||||
(nil)
|
||||
127.0.0.1:6379[5]> get a1
|
||||
(nil)
|
||||
127.0.0.1:6379[5]> expire a1 3
|
||||
(integer) 0
|
||||
127.0.0.1:6379[5]> get a1
|
||||
(nil)
|
||||
127.0.0.1:6379[5]> hset user name itthema
|
||||
(integer) 1
|
||||
127.0.0.1:6379[5]> keys *
|
||||
1) "name"
|
||||
2) "user"
|
||||
127.0.0.1:6379[5]> hmset u2 name itcast age 12
|
||||
OK
|
||||
127.0.0.1:6379[5]> type u2
|
||||
hash
|
||||
127.0.0.1:6379[5]> hkeys user
|
||||
1) "name"
|
||||
127.0.0.1:6379[5]> hkeys u2
|
||||
1) "name"
|
||||
2) "age"
|
||||
127.0.0.1:6379[5]> keys name
|
||||
1) "name"
|
||||
127.0.0.1:6379[5]> keys *
|
||||
1) "name"
|
||||
2) "user"
|
||||
3) "u2"
|
||||
127.0.0.1:6379[5]> hget u2 name
|
||||
"itcast"
|
||||
127.0.0.1:6379[5]> hget u2 age
|
||||
"12"
|
||||
127.0.0.1:6379[5]> hvals u2
|
||||
1) "itcast"
|
||||
2) "12"
|
||||
127.0.0.1:6379[5]> hdel u2 name
|
||||
(integer) 1
|
||||
127.0.0.1:6379[5]> hkeys u2
|
||||
1) "age"
|
||||
127.0.0.1:6379[5]> set a1
|
||||
(error) ERR wrong number of arguments for 'set' command
|
||||
127.0.0.1:6379[5]> lpush a1 a b c
|
||||
(integer) 3
|
||||
127.0.0.1:6379[5]> type a1
|
||||
list
|
||||
127.0.0.1:6379[5]> lrange a1
|
||||
(error) ERR wrong number of arguments for 'lrange' command
|
||||
127.0.0.1:6379[5]> lrange a1 0 3
|
||||
1) "c"
|
||||
2) "b"
|
||||
3) "a"
|
||||
127.0.0.1:6379[5]> rpush a1 0 1
|
||||
(integer) 5
|
||||
127.0.0.1:6379[5]> lrange a1 0 5
|
||||
1) "c"
|
||||
2) "b"
|
||||
3) "a"
|
||||
4) "0"
|
||||
5) "1"
|
||||
127.0.0.1:6379[5]> lrange a1 0 4
|
||||
1) "c"
|
||||
2) "b"
|
||||
3) "a"
|
||||
4) "0"
|
||||
5) "1"
|
||||
127.0.0.1:6379[5]> lrange a1 0 3
|
||||
1) "c"
|
||||
2) "b"
|
||||
3) "a"
|
||||
4) "0"
|
||||
127.0.0.1:6379[5]> linsert a1 before b 3
|
||||
(integer) 6
|
||||
127.0.0.1:6379[5]> lrange a1 0 5
|
||||
1) "c"
|
||||
2) "3"
|
||||
3) "b"
|
||||
4) "a"
|
||||
5) "0"
|
||||
6) "1"
|
||||
127.0.0.1:6379[5]> lrange a1 0 -1
|
||||
1) "c"
|
||||
2) "3"
|
||||
3) "b"
|
||||
4) "a"
|
||||
5) "0"
|
||||
6) "1"
|
||||
127.0.0.1:6379[5]> lset a1 1 z
|
||||
OK
|
||||
127.0.0.1:6379[5]> lrange a1 0 -1
|
||||
1) "c"
|
||||
2) "z"
|
||||
3) "b"
|
||||
4) "a"
|
||||
5) "0"
|
||||
6) "1"
|
||||
127.0.0.1:6379[5]> sadd a3 1 2 3 4
|
||||
(integer) 4
|
||||
127.0.0.1:6379[5]> smembers a2
|
||||
(empty list or set)
|
||||
127.0.0.1:6379[5]> smembers a3
|
||||
1) "1"
|
||||
2) "2"
|
||||
3) "3"
|
||||
4) "4"
|
||||
127.0.0.1:6379[5]> sadd a2 zhangsan wanger lisi
|
||||
(integer) 3
|
||||
127.0.0.1:6379[5]> smembers a2
|
||||
1) "wanger"
|
||||
2) "zhangsan"
|
||||
3) "lisi"
|
||||
127.0.0.1:6379[5]> smembers a3
|
||||
1) "1"
|
||||
2) "2"
|
||||
3) "3"
|
||||
4) "4"
|
||||
127.0.0.1:6379[5]> srem a3 3
|
||||
(integer) 1
|
||||
127.0.0.1:6379[5]> smembers a3
|
||||
1) "1"
|
||||
2) "2"
|
||||
3) "4"
|
||||
127.0.0.1:6379[5]> zadd a4 4 lisi 5 wangwu 6 zhaoliu 3 zhangsan
|
||||
(integer) 4
|
||||
127.0.0.1:6379[5]> keys *
|
||||
1) "name"
|
||||
2) "a2"
|
||||
3) "a1"
|
||||
4) "user"
|
||||
5) "a4"
|
||||
6) "a3"
|
||||
7) "u2"
|
||||
127.0.0.1:6379[5]> type a4
|
||||
zset
|
||||
127.0.0.1:6379[5]> zrange a4 0 -1
|
||||
1) "zhangsan"
|
||||
2) "lisi"
|
||||
3) "wangwu"
|
||||
4) "zhaoliu"
|
||||
127.0.0.1:6379[5]> zscore a4 zhangsan
|
||||
"3"
|
||||
127.0.0.1:6379[5]> zrem z4 zhangsan
|
||||
(integer) 0
|
||||
127.0.0.1:6379[5]> zrem a4 zhangsan
|
||||
(integer) 1
|
||||
127.0.0.1:6379[5]> zrange a4 0 -1
|
||||
1) "lisi"
|
||||
2) "wangwu"
|
||||
3) "zhaoliu"
|
||||
127.0.0.1:6379[5]> zremrangebyscore a4 5 6
|
||||
(integer) 2
|
||||
127.0.0.1:6379[5]> zrange a4 0 -1
|
||||
1) "lisi"
|
||||
|
@ -0,0 +1,25 @@
|
||||
from redis import StrictRedis
|
||||
|
||||
if __name__=="__main__":
|
||||
try:
|
||||
sr = StrictRedis()
|
||||
res = sr.set('name', 'itheima')
|
||||
print(res) # True
|
||||
|
||||
res = sr.get('name')
|
||||
print(res) # b'itheima'
|
||||
|
||||
res = sr.set('name', 'itheima2')
|
||||
print(res) # True
|
||||
|
||||
res = sr.delete('name')
|
||||
print(res) # 1
|
||||
|
||||
res = sr.delete('a1', 'a2')
|
||||
print(res) # 0
|
||||
|
||||
res = sr.keys()
|
||||
print(res)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
@ -0,0 +1,642 @@
|
||||
body{font-family:'Microsoft Yahei';font-size:12px;color:#666;}
|
||||
html,body{height:100%}
|
||||
/* 顶部样式 */
|
||||
.header_con{
|
||||
background-color:#f7f7f7;
|
||||
height:29px;
|
||||
border-bottom:1px solid #dddddd
|
||||
}
|
||||
|
||||
.header{
|
||||
width:1200px;
|
||||
height:29px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
.welcome,.login_info,.login_btn,.user_link{
|
||||
line-height:29px;
|
||||
}
|
||||
|
||||
.login_info{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.login_info em{color:#ff8800}
|
||||
|
||||
.login_btn a,.user_link a{
|
||||
color:#666;
|
||||
}
|
||||
|
||||
.login_btn a:hover,.user_link a:hover{
|
||||
color:#ff8800;
|
||||
}
|
||||
|
||||
.login_btn span,.user_link span{
|
||||
color:#cecece;
|
||||
margin:0 10px;
|
||||
}
|
||||
|
||||
|
||||
/* logo、搜索框、购物车样式 */
|
||||
|
||||
.search_bar{width:1200px;height:115px;margin:0 auto;}
|
||||
.logo{width:150px;height:59px;margin:29px 0 0 17px;}
|
||||
|
||||
.search_con{width:616px;height:38px;border:1px solid #37ab40;margin:34px 0 0 124px;background:url(../images/icons.png) 10px -338px no-repeat;}
|
||||
.search_con .input_text{width:470px;height:34px;border:0px;margin:2px 0 0 36px;outline:none;font-size:12px;color:#737272;font-family:'Microsoft Yahei'}
|
||||
|
||||
.search_con .input_btn{
|
||||
width:100px;height:38px;background-color:#37ab40;border:0px;font-size:14px;color:#fff;font-family:'Microsoft Yahei';outline:none;cursor:pointer;
|
||||
}
|
||||
|
||||
.guest_cart{
|
||||
width:200px;height:40px;margin-top:34px;
|
||||
}
|
||||
|
||||
.guest_cart .cart_name{
|
||||
width:158px;height:38px;line-height:38px;border:1px solid #dddddd;display:block;background:url(../images/icons.png) 13px -300px no-repeat;font-size:14px;color:#37ab40;text-indent:56px;
|
||||
}
|
||||
|
||||
.guest_cart .goods_count{
|
||||
width:40px;height:40px;text-align:center;line-height:40px;font-size:18px;
|
||||
font-weight:bold;color:#fff;background-color:#ff8800;
|
||||
}
|
||||
|
||||
|
||||
/* 菜单、幻灯片样式 */
|
||||
|
||||
.navbar_con{height:40px;border-bottom:2px solid #39a93e}
|
||||
.navbar{width:1200px;margin:0 auto;}
|
||||
.navbar h1{width:200px;height:40px;line-height:40px;text-align: center;font-size:14px;color:#fff;background-color:#39a93e;}
|
||||
|
||||
.navbar .subnav_con{width:200px;height:40px;background-color:#39a93e;position:relative;cursor:pointer;}
|
||||
|
||||
.navbar .subnav_con h1{position:absolute;left:0;top:0;text-align:left;text-indent:40px}
|
||||
.navbar .subnav_con span{display:block;width:16px;height:9px;background:url(../images/down.png) no-repeat;position:absolute;right:27px;top:16px;transition:all 300ms ease-in;
|
||||
}
|
||||
|
||||
.navbar .subnav_con:hover span{transform:rotateZ(180deg)}
|
||||
|
||||
.navbar .subnav_con .subnav{position:absolute;left:0;top:40px;display:none;border-top:2px solid #39a93e;}
|
||||
.navbar .subnav_con:hover .subnav{display:block;}
|
||||
|
||||
|
||||
.navlist{margin-left:34px;}
|
||||
.navlist li{height:40px;float:left;line-height:40px;}
|
||||
.navlist li a{color:#666;font-size:14px}
|
||||
.navlist li a:hover{color:#ff8800}
|
||||
.navlist .interval{margin:0 15px;}
|
||||
|
||||
|
||||
.center_con{width:1200px;height:270px;margin:0 auto;}
|
||||
.subnav{width:198px;height:270px;border-left:1px solid #eee;border-right:1px solid #eee;}
|
||||
.subnav li{height:44px;border-bottom:1px solid #eee;background:url(../images/icons.png) 178px -257px no-repeat #fff;}
|
||||
|
||||
.subnav li a{display:block;height:44px;line-height:44px;text-indent:71px;font-size:14px;color:#333}
|
||||
.subnav li a:hover{color:#ff8800}
|
||||
|
||||
.subnav li .fruit{background:url(../images/icons.png) 28px 0px no-repeat;}
|
||||
.subnav li .seafood{background:url(../images/icons.png) 28px -43px no-repeat;}
|
||||
.subnav li .meet{background:url(../images/icons.png) 28px -86px no-repeat;}
|
||||
.subnav li .egg{background:url(../images/icons.png) 28px -132px no-repeat;}
|
||||
.subnav li .vegetables{background:url(../images/icons.png) 28px -174px no-repeat;}
|
||||
.subnav li .ice{background:url(../images/icons.png) 28px -220px no-repeat;}
|
||||
|
||||
|
||||
.slide{width:760px;height:270px;position:relative;overflow:hidden;}
|
||||
.slide .slide_pics{position:relative;left:0;top:0;width:760px;height:270px;}
|
||||
.slide .slide_pics li{width:760px;height:270px;position:absolute;left:0;top:0}
|
||||
.slide .prev,.slide .next{width:17px;height:23px;background:url(../images/icons.png) left -388px no-repeat;position:absolute;left:11px;top:122px;cursor:pointer;}
|
||||
.slide .next{background-position:left -428px;left:732px;}
|
||||
.points{width:100%;height:11px;position:absolute;left:0;top:250px;text-align:center;}
|
||||
.points li{display:inline-block;width:11px;height:11px;margin:0 5px;background-color:#9f9f9f;border-radius:50%;cursor:pointer;}
|
||||
.points li.active{background-color:#cecece}
|
||||
|
||||
.adv{width:240px;height:270px; overflow:hidden; background-color:gold;}
|
||||
.adv a{display:block;float:left;}
|
||||
|
||||
|
||||
/* 商品列表样式 */
|
||||
|
||||
.list_model{width:1200px;height:340px;margin:15px auto 0;}
|
||||
.list_title{height:40px;border-bottom:2px solid #42ad46}
|
||||
.list_title h3{height:40px;line-height:40px;font-size:16px;color:#37ab40;font-weight:bold;}
|
||||
.list_title .subtitle{height:20px;line-height:20px;margin-top:15px;}
|
||||
.list_title .subtitle span{color:#666;margin:0 10px 0 20px;}
|
||||
.list_title .subtitle a{color:#666;margin:0 5px;}
|
||||
.list_title .subtitle a:hover,.goods_more:hover{color:#ff8800}
|
||||
.goods_more{height:20px;margin-top:15px;color:#666}
|
||||
|
||||
.goods_con{height:300px;}
|
||||
.goods_banner{width:200px;height:300px;}
|
||||
.goods_banner img{width:200px;height:300px;}
|
||||
|
||||
.goods_list{width:1000px;height:299px;border-bottom:1px solid #ededed}
|
||||
.goods_list li{height:299px;width:249px;border-right:1px solid #ededed;float:left}
|
||||
.goods_list li:hover{width:248px;height:297px;border:1px solid gold;}
|
||||
.goods_list li:hover img{opacity:0.8}
|
||||
|
||||
.goods_list li h4{width:200px;height:50px;margin:20px auto 0;text-align:center;}
|
||||
.goods_list li h4 a{font-size:14px;color:#666;font-weight:normal;line-height:24px;}
|
||||
.goods_list li h4 a:hover{color:#ff8800}
|
||||
|
||||
.goods_list li img{display:block;width:180px;height:180px;margin:0 auto;}
|
||||
.goods_list li .prize{text-align:center;font-size:20px;color:#c40000;margin-top:5px;}
|
||||
|
||||
/* 页面底部样式 */
|
||||
.footer{
|
||||
border-top:2px solid #42ad46;
|
||||
margin:30px 0;
|
||||
}
|
||||
|
||||
.foot_link{text-align:center;margin-top:30px;}
|
||||
.foot_link a,.foot_link span{color:#4e4e4e;}
|
||||
.foot_link a:hover{color:#ff8800}
|
||||
.foot_link span{padding:0 10px}
|
||||
.footer p{text-align:center; margin-top:10px;}
|
||||
|
||||
|
||||
/* 二级页面面包屑导航 */
|
||||
.breadcrumb{
|
||||
width:1200px;height:40px;margin:0 auto;
|
||||
}
|
||||
.breadcrumb a{line-height:40px;color:#37ab40}
|
||||
.breadcrumb a:hover{color:#ff8800}
|
||||
.breadcrumb span{line-height:40px;color:#666;padding:0 5px;}
|
||||
|
||||
|
||||
.main_wrap{width:1200px;margin:0 auto;}
|
||||
.l_wrap{width:200px;}
|
||||
.r_wrap{width:980px;}
|
||||
|
||||
|
||||
/* 新品推荐样式 */
|
||||
|
||||
.new_goods{
|
||||
border:1px solid #ededed;
|
||||
border-top:2px solid #37ab40;
|
||||
padding-bottom:10px;
|
||||
}
|
||||
|
||||
.new_goods h3{
|
||||
height:33px;line-height:33px;background-color:#fcfcfc;border-bottom:1px solid #ededed;font-size:14px;font-weight:normal;text-indent:10px;
|
||||
}
|
||||
|
||||
.new_goods ul{width:160px;margin:0 auto;overflow:hidden;}
|
||||
.new_goods li{border-bottom:1px solid #ededed;margin-bottom:-1px;}
|
||||
.new_goods li img{display:block;width:150px;height:150px;margin:10px auto;}
|
||||
.new_goods li h4{width:160px;margin:0 auto;}
|
||||
.new_goods li h4 a{font-weight:normal;color:#666;display:block;width:160px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
|
||||
.new_goods li .prize{font-size:14px;color:#da260e;margin:10px auto;}
|
||||
|
||||
|
||||
|
||||
/* 商品列表样式 */
|
||||
|
||||
.sort_bar{height:30px;background-color:#f0fdec}
|
||||
.sort_bar a{display:block;height:30px;line-height:30px;padding:0 20px;float:left;color:#000}
|
||||
.sort_bar .active{background-color:#37ab40;color:#fff;}
|
||||
|
||||
|
||||
.goods_type_list{
|
||||
margin:10px auto 0;
|
||||
}
|
||||
|
||||
.goods_type_list li{
|
||||
width:196px;
|
||||
float:left;
|
||||
margin-bottom:10px
|
||||
}
|
||||
|
||||
.goods_type_list li img{width:160px;height:160px;display:block;margin:10px auto;}
|
||||
.goods_type_list li h4{width:160px;margin:0 auto;}
|
||||
.goods_type_list li h4 a{font-weight:normal;color:#666;display:block;width:160px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}
|
||||
.operate{width:160px;margin:10px auto;position:relative;}
|
||||
.goods_type_list .operate .prize{color:#da260e; font-size:14px;}
|
||||
.goods_type_list .operate .unit{color:#999;padding-left:5px;}
|
||||
.goods_type_list .operate .add_goods{display:inline-block;width:15px;height:15px;background:url(../images/shop_cart.png);position:absolute;right:0;top:3px;}
|
||||
|
||||
|
||||
/* 分页样式 */
|
||||
|
||||
.pagenation{height:32px;text-align:center;font-size:0;margin:30px auto;}
|
||||
.pagenation a{display:inline-block;border:1px solid #d2d2d2;background-color:#f8f6f7;font-size:12px;padding:7px 10px;color:#666;margin:5px}
|
||||
|
||||
.pagenation .active{background-color:#fff;color:#43a200}
|
||||
|
||||
|
||||
/* 商品详情样式 */
|
||||
.goods_detail_con{
|
||||
width:1198px;
|
||||
height:398px;
|
||||
border:1px solid #ededed;
|
||||
margin:0 auto 20px;
|
||||
}
|
||||
|
||||
.goods_detail_pic{width:350px;height:350px;margin:24px 0 0 24px;}
|
||||
.goods_detail_list{
|
||||
width:730px;height:350px;margin:24px 24px 0 0;
|
||||
}
|
||||
.goods_detail_list h3{font-size:24px;line-height:24px;color:#666;font-weight:normal;}
|
||||
.goods_detail_list p{color:#666;line-height:40px;}
|
||||
.prize_bar{height:72px;background-color:#fff5f5;line-height:72px;}
|
||||
.prize_bar .show_pirze{font-size:20px;color:#ff3e3e;padding-left:20px}
|
||||
.prize_bar .show_pirze em{font-style:normal;font-size:36px;padding-left:10px}
|
||||
.prize_bar .show_unit{padding-left:150px}
|
||||
|
||||
.goods_num{height:52px;margin-top:19px;}
|
||||
.goods_num .num_name{width:70px;height:52px;line-height:52px;}
|
||||
.goods_num .num_add{width:75px;height:50px;border:1px solid #dddddd}
|
||||
.goods_num .num_add input{width:49px;height:50px;text-align:center;line-height:50px;border:0px;outline:none;font-size:14px;color:#666}
|
||||
.goods_num .num_add .add,.goods_num .num_add .minus{width:25px;line-height:25px;text-align:center;border-left:1px solid #ddd;border-bottom:1px solid #ddd;color:#666;font-size:14px}
|
||||
.goods_num .num_add .minus{border-bottom:0px}
|
||||
|
||||
.total{height:35px;line-height:35px;margin-top:25px;}
|
||||
.total em{font-style:normal;color:#ff3e3e;font-size:18px}
|
||||
|
||||
.operate_btn{height:40px;margin-top:35px;font-size:0;position:relative;}
|
||||
.operate_btn .buy_btn,.operate_btn .add_cart{display:inline-block;width:178px;height:38px;border:1px solid #c40000;font-size:14px;color:#c40000;line-height:38px;text-align:center;background-color:#ffeded;}
|
||||
.operate_btn .add_cart{background-color:#c40000;color:#fff;margin-left:10px;position:relative;z-index:10;}
|
||||
|
||||
.add_jump{width:20px;height:20px;background-color:#c40000;position:absolute;left:268px;top:10px;border-radius:50%;z-index:9;display:none;}
|
||||
|
||||
.detail_tab{
|
||||
height:35px;
|
||||
border-bottom:1px solid #37ab40
|
||||
}
|
||||
|
||||
.detail_tab li{height:34px;line-height:34px;padding:0 30px;font-size:14px;color:#333333;float:left;border:1px solid #e8e8e8;border-bottom:0px;cursor:pointer;background-color:#faf8f8}
|
||||
|
||||
.detail_tab li.active{border-top:2px solid #37ab40;position:relative;background-color:#fff;border-left:1px solid #37ab40;border-right:1px solid #37ab40;top:-1px;height:35px;}
|
||||
|
||||
.tab_content dt{margin-top:10px;font-size:16px;color:#044d39}
|
||||
.tab_content dd{line-height:24px;margin-top:5px;}
|
||||
|
||||
|
||||
/* 登录页 */
|
||||
|
||||
.login_top{width:960px;height:130px;margin:0 auto;}
|
||||
.login_logo{display:block;width:193px;height:76px;margin-top:30px;}
|
||||
.login_form_bg{height:480px;background-color:#518e17}
|
||||
.no-mp{margin-top:0px;}
|
||||
.login_form_wrap{width:960px;height:480px;margin:0 auto;}
|
||||
.login_banner{width:381px;height:322px;background:url(../images/login_banner.png) no-repeat;margin-top:90px;}
|
||||
.slogan{width:40px;height:300px;font-size:30px;color:#f0f9e8;text-align:center;line-height:36px;margin:80px 0 0 120px}
|
||||
.login_form{width:368px;height:378px;border:1px solid #c6c6c5;background-color:#fff; margin-top:50px;}
|
||||
|
||||
.login_title{height:60px;width:308px;margin:10px auto;border-bottom:1px solid #e0e0e0;}
|
||||
|
||||
.login_title h1{font-size:24px;height:60px;line-height:60px;color:#a8a8a8;float:left;font-weight:bold;margin-left:44px;}
|
||||
.login_title a{width:100px;height:20px;display:block;font-size:16px;color:#5fb42a;text-indent:26px;background:url(../images/icons02.png) left 5px no-repeat;float:left;margin:20px 0 0 36px}
|
||||
|
||||
.form_input{width:308px;height:250px;margin:20px auto;position:relative;}
|
||||
.name_input,.pass_input{width:306px;height:36px;border:1px solid #e0e0e0;background:url(../images/icons02.png) 280px -41px no-repeat #f8f8f8;outline:none;font-size:14px;text-indent:10px;position: absolute;left:0;top:0}
|
||||
.pass_input{top:65px;background-position:280px -95px;}
|
||||
|
||||
.user_error,.pwd_error{color:#f00;position:absolute;left:0;top:43px;display:none}
|
||||
|
||||
.pwd_error{top:110px;}
|
||||
|
||||
.more_input{position:absolute;left:0;top:130px;width:100%}
|
||||
|
||||
.more_input input{float:left;margin-top:2px;}
|
||||
.more_input label{float:left;margin-left:10px;}
|
||||
.more_input a{float:right;color:#666}
|
||||
.more_input a:hover{color:#ff8800}
|
||||
|
||||
.input_submit{width:100%;height:40px;position:absolute;left:0;top:180px;background-color:#47aa34;color:#fff;font-size:22px;border:0px;font-family:'Microsoft Yahei';cursor:pointer;}
|
||||
|
||||
|
||||
/* 注册页面 */
|
||||
.register_con{
|
||||
width:700px;
|
||||
height:560px;
|
||||
margin:50px auto 0;
|
||||
background:url(../images/interval_line.png) 300px top no-repeat;
|
||||
}
|
||||
|
||||
.l_con{width:300px;}
|
||||
.reg_logo{width:200px;height:76px;float:right;margin-right:30px;}
|
||||
.reg_slogan{width:300px;height:30px;float:right;text-align:right;font-size:24px;color:#69a81e;margin:20px 30px 0 0;}
|
||||
.reg_banner{width:251px;height:329px;background:url(../images/register_banner.png) no-repeat;float:right; margin:20px 10px 0 0;opacity:0.5}
|
||||
|
||||
|
||||
.r_con{width:400px;}
|
||||
.reg_title{width:360px;height:50px;float:left;margin-left:30px;border-bottom:1px solid #e0e0e0}
|
||||
.reg_title h1{height:50px;line-height:50px;float:left;font-size:24px;color:#a8a8a8;font-weight:bold;}
|
||||
.reg_title a{float:right;height:20px;line-height:20px;font-size:16px;color:#5fb42a;padding-right:20px;background:url(../images/icons02.png) 35px 3px no-repeat;margin-top:15px}
|
||||
|
||||
.reg_form{width:360px;margin:30px 0 0 30px;float:left;position:relative;}
|
||||
.reg_form li{height:70px;}
|
||||
.reg_form li label{width:70px;height:40px;line-height:40px;float:left;font-size:14px;color:#a8a8a8}
|
||||
.reg_form li input{width:288px;height:38px;border:1px solid #e0e0e0;float:left;outline:none;text-indent:10px;background-color:#f8f8f8}
|
||||
.reg_form li.agreement input{width:15px;height:15px;float:left;margin-top:13px}
|
||||
.reg_form li.agreement label{width:300px;float:left;margin-left:10px;}
|
||||
.reg_form li.reg_sub input{width:360px;height:40px;background-color:#47aa34;font-size:18px;color:#fff;font-family:'Microsoft Yahei';cursor:pointer;}
|
||||
.reg_form li .error_tip{float:left;height:30px;line-height:30px;margin-left:70px;color:#e62e2e;display:none;}
|
||||
.reg_form li .error_tip2{float:left;height:20px;line-height:20px;color:#e62e2e;display:none;}
|
||||
|
||||
|
||||
.sub_page_name{font-size:18px;color:#666;margin:50px 0 0 20px}
|
||||
|
||||
.total_count{
|
||||
width:1200px;margin:0 auto;height:40px;line-height:40px;font-size:14px;
|
||||
}
|
||||
.total_count em{
|
||||
font-size:16px;color:#ff4200;margin:0 5px;
|
||||
}
|
||||
|
||||
.cart_list_th{width:1198px;border:1px solid #ddd;background-color:#f7f7f7;margin:0 auto;}
|
||||
.cart_list_th li{height:40px;line-height:40px;float:left;text-align:center;}
|
||||
.cart_list_th .col01{width:26%;}
|
||||
.cart_list_th .col02{width:16%;}
|
||||
.cart_list_th .col03{width:13%;}
|
||||
.cart_list_th .col04{width:12%;}
|
||||
.cart_list_th .col05{width:15%;}
|
||||
.cart_list_th .col06{width:18%;}
|
||||
|
||||
.cart_list_td{width:1198px;border:1px solid #ddd;background-color:#edfff9;margin:0 auto;margin-top:-1px;}
|
||||
.cart_list_td li{height:120px;line-height:120px;float:left;text-align:center;}
|
||||
|
||||
.cart_list_td .col01{width:4%;}
|
||||
.cart_list_td .col02{width:12%;}
|
||||
.cart_list_td .col03{width:10%;}
|
||||
.cart_list_td .col04{width:16%;}
|
||||
.cart_list_td .col05{width:13%;}
|
||||
.cart_list_td .col06{width:12%;}
|
||||
.cart_list_td .col07{width:15%;}
|
||||
.cart_list_td .col08{width:18%;}
|
||||
|
||||
.cart_list_td .col02 img{width:100px;height:100px;border:1px solid #ddd;display:block;margin:10px auto 0;}
|
||||
.cart_list_td .col03{height:48px;text-align:left;line-height:24px;margin-top:38px;}
|
||||
.cart_list_td .col03 em{color:#999}
|
||||
.cart_list_td .col08 a{color:#666}
|
||||
|
||||
.cart_list_td .col06 .num_add{width:98px;height:28px;border:1px solid #ddd;margin:40px auto 0;}
|
||||
.cart_list_td .col06 .num_add a{width:29px;height:28px;line-height:28px;background-color:#f3f3f3;font-size:14px;color:#666}
|
||||
.cart_list_td .col06 .num_add input{width:38px;height:28px;text-align:center;line-height:30px;border:0px;display:block;float:left;outline:none;border-left:1px solid #ddd;border-right:1px solid #ddd;}
|
||||
|
||||
|
||||
.settlements{width:1198px;height:78px;border:1px solid #ddd;background-color:#fff4e8;margin:-1px auto 0;}
|
||||
.settlements li{line-height:78px;float:left;}
|
||||
.settlements .col01{width:4%;text-align:center}
|
||||
.settlements .col02{width:12%;}
|
||||
.settlements .col03{width:69%; height:48px; line-height:28px;text-align:right;margin-top:10px;}
|
||||
.settlements .col03 span{color:#ff0000;padding-right:5px}
|
||||
.settlements .col03 em{color:#ff3d3d;font-size:22px;font-weight:bold;}
|
||||
.settlements .col03 span{color:#ff0000;}
|
||||
.settlements .col03 b{color:#ff0000;font-size:14px;padding:0 5px;}
|
||||
|
||||
.settlements .col04{width:14%;text-align:center;float:right;}
|
||||
.settlements .col04 a{display:block;height:78px;background-color:#ff3d3d;text-align:center;line-height:78px;color:#fff;font-size:24px}
|
||||
|
||||
|
||||
.common_title{width:1200px;margin:20px auto 0;font-size:14px;}
|
||||
|
||||
.common_list_con{width:1200px;border:1px solid #dddddd;border-top:2px solid #00bc6f;margin:10px auto 0;background-color:#f7f7f7;position:relative;}
|
||||
|
||||
.common_list_con dl{margin:20px;}
|
||||
.common_list_con dt{font-size:14px;font-weight:bold;margin-bottom:10px}
|
||||
.common_list_con dd input{vertical-align:bottom;margin-right:10px}
|
||||
|
||||
.edit_site{position:absolute; right:20px;top:30px;width:100px;height:30px;background-color:#37ab40;text-align:center;line-height:30px;color:#fff}
|
||||
|
||||
.pay_style_con{margin:20px;}
|
||||
.pay_style_con input{float:left;margin:14px 7px 0 0;}
|
||||
.pay_style_con label{float:left;border:1px solid #ccc;background-color:#fff;padding:10px 10px 10px 40px;margin-right:25px}
|
||||
|
||||
.pay_style_con .cash{background:url(../images/pay_icons.png) 8px top no-repeat #fff;}
|
||||
.pay_style_con .weixin{background:url(../images/pay_icons.png) 6px -36px no-repeat #fff;}
|
||||
|
||||
.pay_style_con .zhifubao{background:url(../images/pay_icons.png) 12px -72px no-repeat #fff;width:50px;height:16px}
|
||||
|
||||
.pay_style_con .bank{background:url(../images/pay_icons.png) 6px -108px no-repeat #fff;}
|
||||
|
||||
|
||||
.goods_list_th{height:40px;border-bottom:1px solid #ccc}
|
||||
.goods_list_th li{float:left;line-height:40px;text-align:center;}
|
||||
.goods_list_th .col01{width:25%}
|
||||
.goods_list_th .col02{width:20%}
|
||||
.goods_list_th .col03{width:25%}
|
||||
.goods_list_th .col04{width:15%}
|
||||
.goods_list_th .col05{width:15%}
|
||||
|
||||
.goods_list_td{height:80px;border-bottom:1px solid #eeeded}
|
||||
.goods_list_td li{float:left;line-height:80px;text-align:center;}
|
||||
.goods_list_td .col01{width:4%}
|
||||
.goods_list_td .col02{width:6%}
|
||||
.goods_list_td .col03{width:15%}
|
||||
.goods_list_td .col04{width:20%}
|
||||
.goods_list_td .col05{width:25%}
|
||||
.goods_list_td .col06{width:15%}
|
||||
.goods_list_td .col07{width:15%}
|
||||
|
||||
.goods_list_td .col02{text-align:right}
|
||||
.goods_list_td .col02 img{width:63px;height:63px;border:1px solid #ddd;display:block;margin:7px 0;float:right;}
|
||||
.goods_list_td .col03{text-align:left;text-indent:20px}
|
||||
|
||||
|
||||
.settle_con{margin:10px}
|
||||
.total_goods_count,.transit,.total_pay{line-height:24px;text-align:right}
|
||||
.total_goods_count em,.total_goods_count b,.transit b,.total_pay b{font-size:14px;color:#ff4200;padding:0 5px;}
|
||||
|
||||
.order_submit{width:1200px;margin:20px auto;}
|
||||
.order_submit a{width:160px;height:40px;line-height:40px;text-align:center;background-color:#47aa34;color:#fff;font-size:16px;display:block;float:right}
|
||||
|
||||
|
||||
.order_list_th{width:1198px;border:1px solid #ddd;background-color:#f7f7f7;margin:20px auto 0;}
|
||||
.order_list_th li{float:left;height:30px;line-height:30px}
|
||||
.order_list_th .col01{width:20%;margin-left:20px}
|
||||
.order_list_th .col02{width:20%}
|
||||
|
||||
|
||||
.order_list_table{
|
||||
width:1200px;
|
||||
border-collapse:collapse;
|
||||
border-spacing:0px;
|
||||
border:1px solid #ddd;
|
||||
margin:-1px auto 0;
|
||||
}
|
||||
|
||||
.order_list_table td{
|
||||
border:1px solid #ddd;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.order_goods_list{border-bottom:1px solid #ddd;margin-bottom:-2px;}
|
||||
.order_goods_list li{float:left; height:80px;line-height:80px;}
|
||||
.order_goods_list .col01{width:20%}
|
||||
.order_goods_list .col01 img{width:60px;height:60px;border:1px solid #ddd;margin:10px auto;}
|
||||
.order_goods_list .col02{width:50%;text-align:left;}
|
||||
.order_goods_list .col02 em{color:#999;margin-left:10px}
|
||||
.order_goods_list .col03{width:10%}
|
||||
.order_goods_list .col04{width:20%}
|
||||
|
||||
.oper_btn{display:inline-block;border:1px solid #ddd;color:#666;padding:5px 10px}
|
||||
|
||||
.popup_con{display:none;}
|
||||
.popup{width:300px;height:150px;border:1px solid #dddddd;border-top:2px solid #00bc6f;background-color:#f7f7f7;position:fixed;
|
||||
left:50%;
|
||||
margin-left:-150px;
|
||||
top:50%;
|
||||
margin-top:-75px;
|
||||
z-index:1000;
|
||||
}
|
||||
|
||||
.popup p{height:150px;line-height:150px;text-align:center;font-size:18px;}
|
||||
|
||||
.mask{width:100%;height:100%;position:fixed;left:0;top:0;background-color:#000;opacity:0.3;z-index:999;}
|
||||
|
||||
|
||||
.main_con{
|
||||
width:1200px;
|
||||
margin:0 auto;
|
||||
background:url(../images/left_bg.jpg) repeat-y;
|
||||
}
|
||||
|
||||
.left_menu_con{
|
||||
width:200px;
|
||||
float:left;
|
||||
}
|
||||
|
||||
.left_menu_con h3{
|
||||
font-size:16px;
|
||||
line-height:40px;
|
||||
border-bottom:1px solid #ddd;
|
||||
text-align:center;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.left_menu_con ul li{
|
||||
line-height:40px;
|
||||
text-align:center;
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.left_menu_con ul li a{
|
||||
color:#666;
|
||||
}
|
||||
|
||||
.left_menu_con ul li .active{
|
||||
color:#ff8800;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.right_content{
|
||||
width:980px;
|
||||
float:right;
|
||||
min-height:500px;
|
||||
}
|
||||
|
||||
.w980{
|
||||
width:980px;
|
||||
}
|
||||
|
||||
.w978{
|
||||
width:978px;
|
||||
}
|
||||
|
||||
|
||||
.common_title2{height:20px;line-height:20px;font-size:16px;margin:10px 0;}
|
||||
.user_info_list{
|
||||
background-color:#f9f9f9;
|
||||
margin:10px 0 15px;
|
||||
padding:10px 0;
|
||||
height:90px;
|
||||
}
|
||||
|
||||
.user_info_list li{
|
||||
line-height:30px;
|
||||
text-indent:30px;
|
||||
font-size:14px;
|
||||
}
|
||||
|
||||
.user_info_list li span{
|
||||
width:100px;
|
||||
float:left;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
.info_con{
|
||||
width:980px;
|
||||
}
|
||||
|
||||
.info_l{
|
||||
width:600px;
|
||||
float:left;
|
||||
}
|
||||
|
||||
.info_r{
|
||||
width:360px;
|
||||
float:right;
|
||||
}
|
||||
|
||||
.site_con{
|
||||
background-color:#f9f9f9;
|
||||
padding:10px 0;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
.site_con dt{
|
||||
font-size:14px;
|
||||
line-height:30px;
|
||||
text-indent:30px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.site_con dd{
|
||||
font-size:14px;
|
||||
line-height:30px;
|
||||
text-indent:30px;
|
||||
}
|
||||
|
||||
.site_con .form_group{
|
||||
height:40px;
|
||||
line-height:40px;
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
.site_con .form_group label{
|
||||
width:100px;
|
||||
float:left;
|
||||
text-align:right;
|
||||
font-size:14px;
|
||||
height:40px;
|
||||
line-height:40px;
|
||||
}
|
||||
|
||||
.site_con .form_group input{
|
||||
width:300px;
|
||||
height:25px;
|
||||
border:1px solid #ddd;
|
||||
float:left;
|
||||
outline:none;
|
||||
margin-top:7px;
|
||||
text-indent:10px;
|
||||
}
|
||||
|
||||
.site_con .form_group2{
|
||||
height:90px;
|
||||
}
|
||||
|
||||
.site_area{
|
||||
width:280px;
|
||||
height:60px;
|
||||
border:1px solid #ddd;
|
||||
outline:none;
|
||||
padding:10px;
|
||||
}
|
||||
.info_submit{
|
||||
width:80px;
|
||||
height:30px;
|
||||
background-color:#37ab40;
|
||||
border:0px;
|
||||
color:#fff;
|
||||
margin:10px 0 10px 100px;
|
||||
cursor:pointer;
|
||||
font-family:'Microsoft Yahei'
|
||||
}
|
||||
|
||||
.stress{
|
||||
color:#ff8800;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/* 把标签默认的间距设为0 */
|
||||
body,ul,ol,p,h1,h2,h3,h4,h5,h6,dl,dd,select,input,textarea,form{margin:0;padding:0}
|
||||
|
||||
/* 让h标签文字大小继承body的文字设置 */
|
||||
h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}
|
||||
|
||||
/* 去掉列表默认的图标 */
|
||||
ul,ol{list-style:none;}
|
||||
|
||||
/* 去掉em默认的斜体 */
|
||||
em{font-style: normal;}
|
||||
|
||||
/* 去掉a标签默认的下划线 */
|
||||
a{text-decoration:none;}
|
||||
|
||||
|
||||
/* 去掉加链接时产生的框线 */
|
||||
img{border:0;}
|
||||
|
||||
/* 清除浮动 */
|
||||
.clearfix:before,.clearfix:after{content:"";display:table}
|
||||
.clearfix:after{clear:both;}
|
||||
.clearfix{zoom:1}
|
||||
|
||||
/* 浮动 */
|
||||
.fl{float:left}
|
||||
.fr{float:right}
|
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 553 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 8.2 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 9.8 KiB |