first init

This commit is contained in:
David 2019-05-06 09:34:08 +08:00
commit c25e540668
217 changed files with 43467 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.DS_Store
frontend/node_modules
/dist
# Editor directories and files
.idea
.vscode
# django
db.sqlite3
Pipfile.lock

View File

151
BookingService/settings.py Normal file
View File

@ -0,0 +1,151 @@
"""
Django settings for BookingService project.
Generated by 'django-admin startproject' using Django 2.1.7.
For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'l%o*o21h_a5^f+3xblzzfch*&2k_4pcak(o#t2@vymeb_mcy3$'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'django_filters',
'user',
'booking',
'utils',
]
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 = 'BookingService.urls'
AUTH_USER_MODEL = 'user.User'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'frontend', 'dist')],
'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 = 'BookingService.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.1/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.1/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static")
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'frontend', 'dist', 'static'),
]
# Django Restfull Framework
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'utils.handler.custom_exception_handler',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
),
'PAGE_SIZE': 10,
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S'],
'DATE_FORMAT': '%Y-%m-%d',
'DATE_INPUT_FORMATS': ['%Y-%m-%d'],
'TIME_FORMAT': '%H:%M:%S',
'TIME_INPUT_FORMATS': ['%H:%M:%S']
}
# ERROR
SILENCED_SYSTEM_CHECKS = [
'rest_framework.W001'
]
SESSION_COOKIE_HTTPONLY = False
APPEND_SLASH = True

26
BookingService/urls.py Normal file
View File

@ -0,0 +1,26 @@
"""BookingService URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/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.contrib import admin
from django.urls import path, include
from django.views.generic.base import TemplateView
urlpatterns = [
path('', TemplateView.as_view(template_name='index.html')),
path('api-auth/', include('rest_framework.urls')),
path('admin/', admin.site.urls),
path('api/user/', include('user.urls', namespace='user')),
path('api/booking/', include('booking.urls', namespace='booking')),
]

16
BookingService/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for BookingService 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.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'BookingService.settings')
application = get_wsgi_application()

14
Pipfile Normal file
View File

@ -0,0 +1,14 @@
[[source]]
name = "pypi"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
verify_ssl = true
[dev-packages]
[packages]
django = "*"
djangorestframework = "*"
django-filter = "*"
[requires]
python_version = "3.7"

1
booking/__init__.py Normal file
View File

@ -0,0 +1 @@
default_app_config = 'booking.apps.BookingConfig'

19
booking/admin.py Normal file
View File

@ -0,0 +1,19 @@
from django.contrib import admin
from . import models
@admin.register(models.Setting)
class SettingAdmin(admin.ModelAdmin):
pass
@admin.register(models.Room)
class RoomAdmin(admin.ModelAdmin):
pass
@admin.register(models.Seat)
class SeatAdmin(admin.ModelAdmin):
pass
@admin.register(models.Booking)
class BookingAdmin(admin.ModelAdmin):
pass

6
booking/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class BookingConfig(AppConfig):
name = 'booking'
verbose_name = '预约'

16
booking/filters.py Normal file
View File

@ -0,0 +1,16 @@
from django_filters import rest_framework as filters
from . import models
class BookingFilter(filters.FilterSet):
min_date = filters.DateFilter(field_name="date", lookup_expr='gte', label='开始日期')
max_date = filters.DateFilter(field_name="date", lookup_expr='lte', label='结束日期')
min_time = filters.TimeFilter(field_name='start_time', lookup_expr='gte', label='开始时间')
max_time = filters.TimeFilter(field_name='end_time', lookup_expr='lte', label='结束时间')
room = filters.UUIDFilter(field_name='room')
status = filters.CharFilter(field_name='status')
class Meta:
model = models.Booking
fields = ['min_date', 'max_date', 'min_time', 'max_time', 'room', 'status']

View File

@ -0,0 +1,100 @@
# Generated by Django 2.1.7 on 2019-04-19 21:07
import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Booking',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_datetime', models.DateTimeField(auto_now_add=True, verbose_name='预约日期时间')),
('start_time', models.TimeField(verbose_name='开始时间')),
('end_time', models.TimeField(verbose_name='结束时间')),
('date', models.DateField(verbose_name='日期')),
('arrive_time', models.TimeField(blank=True, null=True, verbose_name='到达时间')),
('leave_time', models.TimeField(blank=True, null=True, verbose_name='离开时间')),
('is_valid', models.BooleanField(default=True, verbose_name='是否有效')),
('is_cancel', models.BooleanField(default=False, verbose_name='是否取消')),
('cancel_datetime', models.DateTimeField(blank=True, null=True, verbose_name='取消日期时间')),
('cancel_reason', models.CharField(max_length=512, verbose_name='取消理由')),
('cancel_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cancel_by', to=settings.AUTH_USER_MODEL, verbose_name='取消人')),
],
options={
'verbose_name': '预约',
'verbose_name_plural': '预约',
},
),
migrations.CreateModel(
name='Room',
fields=[
('id', models.CharField(default=uuid.uuid1, max_length=36, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(blank=True, max_length=128, verbose_name='名称')),
('decription', models.CharField(blank=True, max_length=512, verbose_name='说明')),
],
options={
'verbose_name': '房间',
'verbose_name_plural': '房间',
},
),
migrations.CreateModel(
name='Seat',
fields=[
('id', models.CharField(default=uuid.uuid1, max_length=36, primary_key=True, serialize=False, verbose_name='ID')),
('creaeted_datetime', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间')),
],
options={
'verbose_name': '座位',
'verbose_name_plural': '座位',
},
),
migrations.CreateModel(
name='Setting',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('pre_booking_interval_day', models.IntegerField(default=30, verbose_name='提前预约时间间隔(天)')),
('booking_interval', models.IntegerField(default=30, verbose_name='预约间隔(分钟)')),
('start_time', models.TimeField(default=datetime.time(8, 0), verbose_name='开始时间')),
('end_time', models.TimeField(default=datetime.time(21, 0), verbose_name='结束时间')),
('punish_point', models.IntegerField(default=5, verbose_name='惩罚积分数')),
('reward_point', models.IntegerField(default=10, verbose_name='奖励积分数')),
],
options={
'verbose_name': '预约设置',
'verbose_name_plural': '预约设置',
},
),
migrations.AddField(
model_name='booking',
name='room',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间'),
),
migrations.AddField(
model_name='booking',
name='seat',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='booking.Seat', verbose_name='座位'),
),
migrations.AddField(
model_name='booking',
name='seats',
field=models.ManyToManyField(related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
migrations.AddField(
model_name='booking',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user', to=settings.AUTH_USER_MODEL, verbose_name='预约人'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-22 15:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='room',
name='decription',
field=models.TextField(blank=True, max_length=512, verbose_name='说明'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.1.7 on 2019-04-22 15:36
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('booking', '0002_auto_20190422_1505'),
]
operations = [
migrations.RemoveField(
model_name='booking',
name='is_valid',
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 2.1.7 on 2019-04-22 15:56
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('booking', '0003_remove_booking_is_valid'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='cancel_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cancel_by', to=settings.AUTH_USER_MODEL, verbose_name='取消人'),
),
migrations.AlterField(
model_name='booking',
name='cancel_reason',
field=models.CharField(blank=True, max_length=512, null=True, verbose_name='取消理由'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-22 15:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0004_auto_20190422_1556'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='seats',
field=models.ManyToManyField(blank=True, null=True, related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.1.7 on 2019-04-22 16:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('booking', '0005_auto_20190422_1556'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='room',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Room', verbose_name='房间'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-22 16:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0006_auto_20190422_1636'),
]
operations = [
migrations.AlterField(
model_name='booking',
name='seats',
field=models.ManyToManyField(blank=True, related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 2.1.7 on 2019-04-22 16:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0007_auto_20190422_1636'),
]
operations = [
migrations.RemoveField(
model_name='booking',
name='seat',
),
migrations.AlterField(
model_name='booking',
name='seats',
field=models.ManyToManyField(related_name='seats', to='booking.Seat', verbose_name='多个座位'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-22 17:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('booking', '0008_auto_20190422_1649'),
]
operations = [
migrations.RenameField(
model_name='seat',
old_name='creaeted_datetime',
new_name='created_datetime',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-26 19:47
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('booking', '0009_auto_20190422_1734'),
]
operations = [
migrations.RenameField(
model_name='room',
old_name='decription',
new_name='description',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-04-27 10:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0010_auto_20190426_1947'),
]
operations = [
migrations.AddField(
model_name='seat',
name='name',
field=models.CharField(blank=True, max_length=128, verbose_name='名称'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 2.1.7 on 2019-04-30 09:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('booking', '0011_seat_name'),
]
operations = [
migrations.RemoveField(
model_name='booking',
name='is_cancel',
),
migrations.AddField(
model_name='booking',
name='status',
field=models.CharField(choices=[('PENDING', '正在申请'), ('SUCCESS', '申请成功'), ('FAILED', '申请失败'), ('CANCELED', '被取消')], default='PENDING', max_length=10, verbose_name='状态'),
),
]

View File

79
booking/models.py Normal file
View File

@ -0,0 +1,79 @@
import datetime
import uuid
from django.db import models
from user.models import User
class Setting(models.Model):
pre_booking_interval_day = models.IntegerField('提前预约时间间隔(天)', default=30)
booking_interval = models.IntegerField('预约间隔(分钟)', default=30)
start_time = models.TimeField('开始时间', default=datetime.time(8, 0))
end_time = models.TimeField('结束时间', default=datetime.time(21, 0))
punish_point = models.IntegerField('惩罚积分数', default=5)
reward_point = models.IntegerField('奖励积分数', default=10)
class Meta:
verbose_name = '预约设置'
verbose_name_plural = verbose_name
def __str__(self):
return f'{self.start_time}-{self.end_time}'
class Room(models.Model):
id = models.CharField('ID', max_length=36, default=uuid.uuid1, primary_key=True)
name = models.CharField('名称', max_length=128, blank=True)
description = models.TextField('说明', max_length=512, blank=True)
class Meta:
verbose_name = '房间'
verbose_name_plural = verbose_name
def __str__(self):
return f'{self.name}'
class Seat(models.Model):
id = models.CharField('ID', max_length=36, default=uuid.uuid1, primary_key=True)
name = models.CharField('名称', max_length=128, blank=True)
room = models.ForeignKey(verbose_name='房间', to=Room, on_delete=models.CASCADE)
created_datetime = models.DateTimeField('创建时间', auto_now_add=True)
class Meta:
verbose_name = '座位'
verbose_name_plural = verbose_name
def __str__(self):
return f'{self.room.id}-{self.room.name}-{self.id}'
class Booking(models.Model):
STATUS_CHIOCE = [
('PENDING', '正在申请'),
('SUCCESS', '申请成功'),
('FAILED', '申请失败'),
('CANCELED', '被取消'),
]
user = models.ForeignKey(verbose_name='预约人', to=User, on_delete=models.CASCADE, related_name='user')
room = models.ForeignKey(verbose_name='房间', to=Room, on_delete=models.CASCADE)
seats = models.ManyToManyField(verbose_name='多个座位', to=Seat, related_name='seats')
created_datetime = models.DateTimeField('预约日期时间', auto_now_add=True)
start_time = models.TimeField('开始时间')
end_time = models.TimeField('结束时间')
date = models.DateField('日期')
arrive_time = models.TimeField('到达时间', blank=True, null=True)
leave_time = models.TimeField('离开时间', blank=True, null=True)
status = models.CharField('状态', choices=STATUS_CHIOCE, max_length=10, default='PENDING')
cancel_datetime = models.DateTimeField('取消日期时间', blank=True, null=True)
cancel_reason = models.CharField('取消理由', max_length=512, blank=True, null=True)
cancel_by = models.ForeignKey(verbose_name='取消人', to=User, on_delete=models.CASCADE, related_name='cancel_by',
blank=True, null=True)
class Meta:
verbose_name = '预约'
verbose_name_plural = verbose_name
def __str__(self):
return f'{self.user.username}-{self.date}'

55
booking/serializers.py Normal file
View File

@ -0,0 +1,55 @@
from rest_framework import serializers
from user.serializers import UserSerializer
from . import models
class SettingSerializer(serializers.ModelSerializer):
class Meta:
model = models.Setting
fields = '__all__'
extra_kwargs = {
'pre_booking_interval_day': {
'min_value': 1
},
'punish_point': {
'min_value': 1
},
'reward_point': {
'min_value': 1
},
}
# TODO
def validate_booking_interval(self, value):
if (not value in [30, 60]):
raise serializers.ValidationError('请确保该值等于30或者60。')
return value
class RoomSerializer(serializers.ModelSerializer):
class Meta:
model = models.Room
fields = '__all__'
class SeatSerializer(serializers.ModelSerializer):
class Meta:
model = models.Seat
fields = '__all__'
class BookingSerializer(serializers.ModelSerializer):
user = UserSerializer()
room = RoomSerializer()
seats = SeatSerializer(many=True)
class Meta:
model = models.Booking
fields = '__all__'
class BookingCreateSerializer(serializers.ModelSerializer):
class Meta:
model = models.Booking
fields = '__all__'

3
booking/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

18
booking/urls.py Normal file
View File

@ -0,0 +1,18 @@
from django.urls import path
from . import views
app_name = 'booking'
urlpatterns = [
path('', views.Booking.as_view()),
path('<uuid:pk>/', views.BookingDetail.as_view()),
path('statusList/', views.BookingStatusList.as_view()),
path('setting/<int:pk>/', views.SettingDetail.as_view()),
path('room/', views.Room.as_view()),
path('room/bookingStatus/', views.RoomBookingStatus.as_view()),
path('room/<uuid:pk>/', views.RoomDetail.as_view()),
path('room/<uuid:pk>/bookingStatus/', views.RoomBookingStatusDetail.as_view()),
path('seat/', views.Seat.as_view()),
path('seat/<uuid:pk>/', views.SeatDetail.as_view()),
]

241
booking/views.py Normal file
View File

@ -0,0 +1,241 @@
import datetime
from django.db.models import QuerySet
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics
from rest_framework import permissions
from rest_framework.exceptions import ValidationError
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from . import models, serializers, filters
class SettingDetail(generics.RetrieveUpdateAPIView):
"""
获得设置
更新设置
"""
queryset = models.Setting.objects.all()
serializer_class = serializers.SettingSerializer
permission_classes = (permissions.IsAdminUser,)
class Room(generics.ListAPIView):
"""
获得room列表
"""
queryset = models.Room.objects.all().order_by('name')
serializer_class = serializers.RoomSerializer
permission_classes = (permissions.IsAuthenticated,)
class RoomDetail(generics.RetrieveUpdateAPIView):
"""
获得指定room
更新指定room
"""
queryset = models.Room.objects.all()
serializer_class = serializers.RoomSerializer
permission_classes = (permissions.IsAdminUser,)
class Seat(generics.ListCreateAPIView):
"""
获得seat列表
新建seat
"""
queryset = models.Seat.objects.all().order_by('-created_datetime')
serializer_class = serializers.SeatSerializer
permission_classes = (permissions.IsAdminUser,)
filter_backends = (DjangoFilterBackend,)
filterset_fields = ['room__id']
def perform_create(self, serializer):
name = serializer.validated_data.get('name')
r = serializer.validated_data.get('room')
room = models.Room.objects.get(id=r.id)
models.Seat.objects.create(room=room, name=name)
class SeatDetail(generics.DestroyAPIView):
"""
删除指定seat
"""
queryset = models.Seat.objects.all()
serializer_class = serializers.SeatSerializer
permission_classes = (permissions.IsAdminUser,)
class Booking(generics.ListCreateAPIView):
"""
获得booking列表
新建booking
"""
queryset = models.Booking.objects.all().order_by('id')
# serializer_class = serializers.BookingSerializer
permission_classes = (permissions.IsAuthenticated,)
pagination_class = PageNumberPagination
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
filterset_class = filters.BookingFilter
search_fields = [
'user__username',
'room__name',
'date'
]
ordering_fields = [
'id',
'date',
'start_time',
'end_time',
]
def perform_create(self, serializer):
print(serializer.data)
return Response('')
def get_serializer_class(self):
if self.request.stream and self.request.stream.method == 'POST':
return serializers.BookingCreateSerializer
else:
return serializers.BookingSerializer
class BookingDetail(generics.RetrieveUpdateDestroyAPIView):
"""
获得指定booking
更新指定booking
删除指定booking
"""
queryset = models.Booking.objects.all()
serializer_class = serializers.BookingSerializer
permission_classes = (permissions.IsAuthenticated,)
class BookingStatusList(generics.GenericAPIView):
"""
获得预约状态列表
"""
queryset = QuerySet()
permission_classes = (permissions.IsAuthenticated,)
def get(self, request):
status_list = []
for status in models.Booking.STATUS_CHIOCE:
status_list.append({
'id': status[0],
'name': status[1]
})
return Response(status_list)
class RoomBookingStatus(generics.GenericAPIView):
"""
获得房间预约信息
"""
queryset = QuerySet()
serializer_class = (serializers.RoomSerializer)
permission_classes = (permissions.IsAuthenticated,)
def get(self, request):
try:
date = datetime.datetime.strptime(request.query_params.get('date'), '%Y-%m-%d').date()
except Exception as e:
raise ValidationError(e)
setting = models.Setting.objects.get(id=1)
start_datetime = datetime.datetime.combine(date, setting.start_time)
end_datetime = datetime.datetime.combine(date, setting.end_time)
step = datetime.timedelta(minutes=setting.booking_interval)
count_per_seat = int((end_datetime - start_datetime) / step)
count_total = 0
booked_count_total = 0
booking_count_total = 0
room_booking_status_list = []
for room in models.Room.objects.all():
seat_list = room.seat_set.all()
count_per_room = 0
booked_count = 0
booking_count = 0
for seat in seat_list:
count_per_room += count_per_seat
booking_list = models.Booking.objects.filter(date=date, room=room, seats__id=seat.id, status='SUCCESS')
for booking in booking_list:
s_datetime = datetime.datetime.combine(date, booking.start_time)
e_datetime = datetime.datetime.combine(date, booking.end_time)
s = int((s_datetime - start_datetime) / step)
e = int((e_datetime - start_datetime) / step)
booked_count += e - s
booking_count += 1
count_total += count_per_room
booked_count_total += booked_count
booking_count_total += booking_count
room_booking_status_list.append({
**self.get_serializer(room).data,
'count': count_per_room,
'booked_count': booked_count,
'booking_count': booking_count
})
return Response({
'count_total': count_total,
'booked_count_total': booked_count_total,
'booking_count_total': booking_count_total,
'results': room_booking_status_list
})
class RoomBookingStatusDetail(generics.GenericAPIView):
"""
获得指定房间的预约信息
"""
queryset = QuerySet()
serializer_class = (serializers.RoomSerializer)
permission_classes = (permissions.IsAuthenticated,)
def get(self, request, pk):
try:
date = datetime.datetime.strptime(request.query_params.get('date'), '%Y-%m-%d').date()
except Exception as e:
raise ValidationError(e)
room = get_object_or_404(models.Room, id=pk)
setting = models.Setting.objects.get(id=1)
count_total = 0
booked_count_total = 0
seat_list = []
for seat in room.seat_set.all():
# 初始化数组
step = datetime.timedelta(minutes=setting.booking_interval)
booking_status_list = []
start_datetime = datetime.datetime.combine(date, setting.start_time)
end_datetime = start_datetime + step
while end_datetime <= datetime.datetime.combine(date, setting.end_time):
booking_status_list.append({
'start_time': start_datetime.strftime('%H:%M:%S'),
'end_time': end_datetime.strftime('%H:%M:%S'),
'booking': None
})
count_total += 1
start_datetime = end_datetime
end_datetime += step
# 检查预约记录
for booking in models.Booking.objects.filter(date=date, room=room, seats__id=seat.id, status='SUCCESS'):
s_datetime = datetime.datetime.combine(date, booking.start_time)
e_datetime = datetime.datetime.combine(date, booking.end_time)
s = int((s_datetime - start_datetime) / step)
e = int((e_datetime - start_datetime) / step)
booked_count_total += e - s
for i in range(s, e):
booking_status_list[i]['booking'] = serializers.BookingSerializer(booking).data
seat_list.append({
'id': seat.id,
'name': seat.name,
'booking_status_list': booking_status_list
})
return Response({
**self.get_serializer(room).data,
'date': date,
'count_total': count_total,
'booked_count_total': booked_count_total,
'seat_list': seat_list
})

0
doc/api/setting.md Normal file
View File

61
doc/api/status_code.md Normal file
View File

@ -0,0 +1,61 @@
### `200`
正确返回信息,有返回追
## `201`
创建成功,有返回值
## `400`
验证失败,有返回值,返回错误
```json
{
"detail": Object
}
```
## `401`
## `402`
## `403`
访问被拒绝,用户没有登录或没有权限,有返回值,返回错误
```json
{
"detail": Object
}
```
## `404`
找不到请求的对象,有返回值,返回错误
```json
{
"detail": Object
}
```
## `405`
请求方法错误,有返回值,返回错误
```json
{
"detail": Object
}
```
## `500`
服务器错误,无返回值

155
doc/api/template.md Normal file
View File

@ -0,0 +1,155 @@
`GET` /api/collection/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
`post` /api/collection/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
`get` /api/collection/:id
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
`put` /api/collection/:id
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
`delete` /api/collection/:id
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```

262
doc/api/user.md Normal file
View File

@ -0,0 +1,262 @@
## Model
```json
{
"id": Number,
"password": "", // wirte_only
"last_login": "", // "2019-04-20 13:23:53"
"is_superuser": Boolean,
"username": "",
"email": "",
"is_staff": Boolean,
"is_active": Boolean,
"date_joined": "", // "2019-04-19 21:08:29"
"role": "",
"point": Number,
"groups": [],
"user_permissions": []
}
```
## Rest
### `GET` /api/user/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
### `post` /api/user/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
### `get` /api/user/:id/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
### `put` /api/user/:id/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
### `delete` /api/user/:id/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
## Action
### `post` /api/user/login/
parameters:
```json
```
request:
```json
{
"username": "",
"password": ""
}
```
response:
```json
{
<model>
}
```
error:
```json
{
}
```
### `get` /api/user/logout/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
}
```
error:
```json
{
}
```
### `get` /api/user/getInfo/
parameters:
```json
```
request:
```json
{
}
```
response:
```json
{
<model>
}
```
error:
```json
{
}
```

39
frontend/.editorconfig Normal file
View File

@ -0,0 +1,39 @@
[*]
charset=utf-8
end_of_line=crlf
insert_final_newline=false
indent_style=space
indent_size=2
[{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}]
indent_style=space
indent_size=2
[{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}]
indent_style=space
indent_size=2
[{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}]
indent_style=space
indent_size=2
[*.svg]
indent_style=space
indent_size=2
[*.js.map]
indent_style=space
indent_size=2
[*.less]
indent_style=space
indent_size=2
[*.vue]
indent_style=space
indent_size=2
[{.analysis_options,*.yml,*.yaml}]
indent_style=space
indent_size=2

1
frontend/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
public/* linguist-vendored

5
frontend/.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"printWidth": 120,
"semi": false,
"singleQuote": true
}

13
frontend/babel.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
presets: [
'@vue/app'
]
// ,
// plugins: [
// [ 'import', {
// 'libraryName': 'ant-design-vue',
// 'libraryDirectory': 'es',
// 'style': true // `style: true` 会加载 less 文件
// } ]
// ]
}

7684
frontend/dist/color.less vendored Normal file

File diff suppressed because it is too large Load Diff

1
frontend/dist/index.html vendored Normal file
View File

@ -0,0 +1 @@
<!DOCTYPE html><html lang=zh-cmn-Hans><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><link rel=icon href=/logo.png><title>建筑咨询管理系统</title><style>#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}</style><link href=/static/css/fail.4c41990b.css rel=prefetch><link href=/static/css/user.d3b41a87.css rel=prefetch><link href=/static/js/asider.0aaae6b5.js rel=prefetch><link href=/static/js/chunk-2d209ade.3e698d10.js rel=prefetch><link href=/static/js/fail.40be6fc6.js rel=prefetch><link href=/static/js/user.d638c020.js rel=prefetch><link href=/static/css/app.c99c38d5.css rel=preload as=style><link href=/static/css/chunk-vendors.cc2a8f53.css rel=preload as=style><link href=/static/js/app.c3c48851.js rel=preload as=script><link href=/static/js/chunk-vendors.cd3c5264.js rel=preload as=script><link href=/static/css/chunk-vendors.cc2a8f53.css rel=stylesheet><link href=/static/css/app.c99c38d5.css rel=stylesheet></head><body><noscript><strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app><div id=preloadingAnimation><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div></div><script src=/static/js/chunk-vendors.cd3c5264.js></script><script src=/static/js/app.c3c48851.js></script></body></html>

1
frontend/dist/loading/loading.css vendored Normal file
View File

@ -0,0 +1 @@
#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}

1
frontend/dist/loading/loading.html vendored Normal file
View File

@ -0,0 +1 @@
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>

View File

@ -0,0 +1,5 @@
<div class="preloading-animate">
<div class="preloading-wrapper">
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>
</div>
</div>

View File

@ -0,0 +1 @@
.preloading-animate{background:#ffffff;width:100%;height:100%;position:fixed;left:0;top:0;z-index:299;}.preloading-animate .preloading-wrapper{position:absolute;width:5rem;height:5rem;left:50%;top:50%;transform:translate(-50%,-50%);}.preloading-animate .preloading-wrapper .preloading-balls{font-size:5rem;}

View File

@ -0,0 +1 @@
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
frontend/dist/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.exception[data-v-e85f82bc]{min-height:500px;height:80%;-webkit-box-align:center;-ms-flex-align:center;align-items:center;text-align:center;margin-top:150px}.exception .img[data-v-e85f82bc]{display:inline-block;padding-right:52px;zoom:1}.exception .img img[data-v-e85f82bc]{height:360px;max-width:430px}.exception .content[data-v-e85f82bc]{display:inline-block;-webkit-box-flex:1;-ms-flex:auto;flex:auto}.exception .content h1[data-v-e85f82bc]{color:#434e59;font-size:72px;font-weight:600;line-height:72px;margin-bottom:24px}.exception .content .desc[data-v-e85f82bc]{color:rgba(0,0,0,.45);font-size:20px;line-height:28px;margin-bottom:16px}.mobile .exception[data-v-e85f82bc]{margin-top:30px}.mobile .exception .img[data-v-e85f82bc]{padding-right:unset}.mobile .exception .img img[data-v-e85f82bc]{height:40%;max-width:80%}

View File

@ -0,0 +1 @@
.user-layout-login label[data-v-38784526]{font-size:14px}.user-layout-login button.login-button[data-v-38784526]{padding:0 15px;font-size:16px;height:40px;width:100%}

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
<title>Group 28 Copy 5</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="62.1023273%" y1="0%" x2="108.19718%" y2="37.8635764%" id="linearGradient-1">
<stop stop-color="#4285EB" offset="0%"></stop>
<stop stop-color="#2EC7FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="69.644116%" y1="0%" x2="54.0428975%" y2="108.456714%" id="linearGradient-2">
<stop stop-color="#29CDFF" offset="0%"></stop>
<stop stop-color="#148EFF" offset="37.8600687%"></stop>
<stop stop-color="#0A60FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="69.6908165%" y1="-12.9743587%" x2="16.7228981%" y2="117.391248%" id="linearGradient-3">
<stop stop-color="#FA816E" offset="0%"></stop>
<stop stop-color="#F74A5C" offset="41.472606%"></stop>
<stop stop-color="#F51D2C" offset="100%"></stop>
</linearGradient>
<linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-4">
<stop stop-color="#FA8E7D" offset="0%"></stop>
<stop stop-color="#F74A5C" offset="51.2635191%"></stop>
<stop stop-color="#F51D2C" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="logo" transform="translate(-20.000000, -20.000000)">
<g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)">
<g id="Group-27-Copy-3">
<g id="Group-25" fill-rule="nonzero">
<g id="2">
<path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-1)"></path>
<path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-2)"></path>
</g>
<path d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z" id="Shape" fill="url(#linearGradient-3)"></path>
</g>
<ellipse id="Combined-Shape" fill="url(#linearGradient-4)" cx="100.519339" cy="100.436681" rx="23.6001926" ry="23.580786"></ellipse>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["asider","chunk-2d209ade"],{a9a1:function(e,t,n){"use strict";n.r(t);var a=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("a-card",[e._v("\n 123\n")])},r=[],u={name:"Implement"},l=u,c=n("2877"),o=Object(c["a"])(l,a,r,!1,null,null,null);t["default"]=o.exports},b7bc:function(e,t,n){"use strict";n.r(t);var a=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("page-layout",{attrs:{title:"项目"}})},r=[],u=n("b445"),l={name:"Project",components:{PageLayout:u["a"]},data:function(){return{}}},c=l,o=n("2877"),s=Object(o["a"])(c,a,r,!1,null,null,null);t["default"]=s.exports}}]);
//# sourceMappingURL=asider.0aaae6b5.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["webpack:///./src/views/implement/Implement.vue?ad18","webpack:///src/views/implement/Implement.vue","webpack:///./src/views/implement/Implement.vue?6e80","webpack:///./src/views/implement/Implement.vue","webpack:///./src/views/project/Project.vue?7a17","webpack:///src/views/project/Project.vue","webpack:///./src/views/project/Project.vue?27a9","webpack:///./src/views/project/Project.vue"],"names":["render","_vm","this","_h","$createElement","_c","_self","_v","staticRenderFns","Implementvue_type_script_lang_js_","name","implement_Implementvue_type_script_lang_js_","component","Object","componentNormalizer","__webpack_exports__","attrs","title","Projectvue_type_script_lang_js_","components","PageLayout","data","project_Projectvue_type_script_lang_js_"],"mappings":"gIAAA,IAAAA,EAAA,WAA0B,IAAAC,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,UAAAJ,EAAAM,GAAA,gBACzFC,EAAA,GCMAC,EAAA,CACAC,KAAA,aCRkWC,EAAA,cCOlWC,EAAgBC,OAAAC,EAAA,KAAAD,CACdF,EACAX,EACAQ,GACF,EACA,KACA,KACA,MAIeO,EAAA,WAAAH,oDClBf,IAAAZ,EAAA,WAA0B,IAAAC,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,eAAyBW,MAAA,CAAOC,MAAA,SACzHT,EAAA,eCOAU,EAAA,CACAR,KAAA,UACAS,WAAA,CACAC,aAAA,MAEAC,KALA,WAMA,WCdgWC,EAAA,cCOhWV,EAAgBC,OAAAC,EAAA,KAAAD,CACdS,EACAtB,EACAQ,GACF,EACA,KACA,KACA,MAIeO,EAAA,WAAAH","file":"static/js/asider.0aaae6b5.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('a-card',[_vm._v(\"\\n 123\\n\")])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","<template>\r\n <a-card>\r\n 123\r\n </a-card>\r\n</template>\r\n\r\n<script>\r\nexport default {\r\n name: 'Implement'\r\n}\r\n</script>\r\n\r\n<style lang=\"stylus\">\r\n\r\n</style>\r\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Implement.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Implement.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Implement.vue?vue&type=template&id=23cbbf94&\"\nimport script from \"./Implement.vue?vue&type=script&lang=js&\"\nexport * from \"./Implement.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('page-layout',{attrs:{\"title\":'项目'}})}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","<template>\r\n <page-layout :title=\"'项目'\">\r\n </page-layout>\r\n</template>\r\n\r\n<script>\r\nimport PageLayout from '../../components/page/PageLayout'\r\n\r\nexport default {\r\n name: 'Project',\r\n components: {\r\n PageLayout\r\n },\r\n data () {\r\n return {\r\n }\r\n }\r\n}\r\n</script>\r\n\r\n<style lang=\"stylus\">\r\n\r\n</style>\r\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Project.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Project.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Project.vue?vue&type=template&id=55f65630&\"\nimport script from \"./Project.vue?vue&type=script&lang=js&\"\nexport * from \"./Project.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""}

View File

@ -0,0 +1,2 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d209ade"],{a9a1:function(n,e,a){"use strict";a.r(e);var t=function(){var n=this,e=n.$createElement,a=n._self._c||e;return a("a-card",[n._v("\n 123\n")])},c=[],l={name:"Implement"},u=l,r=a("2877"),s=Object(r["a"])(u,t,c,!1,null,null,null);e["default"]=s.exports}}]);
//# sourceMappingURL=chunk-2d209ade.3e698d10.js.map

View File

@ -0,0 +1 @@
{"version":3,"sources":["webpack:///./src/views/implement/Implement.vue?ad18","webpack:///src/views/implement/Implement.vue","webpack:///./src/views/implement/Implement.vue?6e80","webpack:///./src/views/implement/Implement.vue"],"names":["render","_vm","this","_h","$createElement","_c","_self","_v","staticRenderFns","Implementvue_type_script_lang_js_","name","implement_Implementvue_type_script_lang_js_","component","Object","componentNormalizer","__webpack_exports__"],"mappings":"uHAAA,IAAAA,EAAA,WAA0B,IAAAC,EAAAC,KAAaC,EAAAF,EAAAG,eAA0BC,EAAAJ,EAAAK,MAAAD,IAAAF,EAAwB,OAAAE,EAAA,UAAAJ,EAAAM,GAAA,gBACzFC,EAAA,GCMAC,EAAA,CACAC,KAAA,aCRkWC,EAAA,cCOlWC,EAAgBC,OAAAC,EAAA,KAAAD,CACdF,EACAX,EACAQ,GACF,EACA,KACA,KACA,MAIeO,EAAA,WAAAH","file":"static/js/chunk-2d209ade.3e698d10.js","sourcesContent":["var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('a-card',[_vm._v(\"\\n 123\\n\")])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","<template>\r\n <a-card>\r\n 123\r\n </a-card>\r\n</template>\r\n\r\n<script>\r\nexport default {\r\n name: 'Implement'\r\n}\r\n</script>\r\n\r\n<style lang=\"stylus\">\r\n\r\n</style>\r\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Implement.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Implement.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Implement.vue?vue&type=template&id=23cbbf94&\"\nimport script from \"./Implement.vue?vue&type=script&lang=js&\"\nexport * from \"./Implement.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["fail"],{"790e":function(t,e,s){"use strict";var c=s("b449"),i=s.n(c);i.a},b449:function(t,e,s){},cc89:function(t,e,s){"use strict";s.r(e);var c=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("exception-page",{attrs:{type:"404"}})},i=[],n=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"exception"},[s("div",{staticClass:"img"},[s("img",{attrs:{src:t.config[t.type].img}})]),s("div",{staticClass:"content"},[s("h1",[t._v(t._s(t.config[t.type].title))]),s("div",{staticClass:"desc"},[t._v(t._s(t.config[t.type].desc))]),s("div",{staticClass:"action"},[s("a-button",{attrs:{type:"primary"},on:{click:t.handleToHome}},[t._v("返回首页")])],1)])])},a=[],o={403:{img:"https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg",title:"403",desc:"抱歉,你无权访问该页面"},404:{img:"https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg",title:"404",desc:"抱歉,你访问的页面不存在或仍在开发中"},500:{img:"https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg",title:"500",desc:"抱歉,服务器出错了"}},p=o,l={name:"Exception",props:{type:{type:String,default:"404"}},data:function(){return{config:p}},methods:{handleToHome:function(){this.$router.push({name:"dashboard"})}}},r=l,d=(s("790e"),s("2877")),u=Object(d["a"])(r,n,a,!1,null,"e85f82bc",null),m=u.exports,g={components:{ExceptionPage:m}},f=g,h=Object(d["a"])(f,c,i,!1,null,"4ff23dea",null);e["default"]=h.exports}}]);
//# sourceMappingURL=fail.40be6fc6.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["user"],{"4c5b":function(t,e,a){},"698e":function(t,e,a){"use strict";var r=a("4c5b"),i=a.n(r);i.a},ac2a:function(t,e,a){"use strict";a.r(e);var r=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"main"},[a("a-form",{ref:"formLogin",staticClass:"user-layout-login",attrs:{id:"formLogin",form:t.form},on:{submit:t.handleSubmit}},[a("a-form-item",{staticStyle:{"margin-top":"24px","text-align":"center","font-size":"20px"}},[t._v("\n 用户登录\n ")]),a("a-form-item",{staticStyle:{"margin-top":"40px"}},[a("a-input",{directives:[{name:"decorator",rawName:"v-decorator",value:["username",{rules:[{required:!0,message:"请输入用户名"}],validateTrigger:"blur"}],expression:"[\n 'username',\n {rules: [{ required: true, message: '请输入用户名' }], validateTrigger: 'blur'}\n ]"}],attrs:{size:"large",type:"text",autocomplete:"true",placeholder:"用户名"}},[a("a-icon",{style:{color:"rgba(0,0,0,.25)"},attrs:{slot:"prefix",type:"user"},slot:"prefix"})],1)],1),a("a-form-item",[a("a-input",{directives:[{name:"decorator",rawName:"v-decorator",value:["password",{rules:[{required:!0,message:"请输入密码"}],validateTrigger:"blur"}],expression:"[\n 'password',\n {rules: [{ required: true, message: '请输入密码' }], validateTrigger: 'blur'}\n ]"}],attrs:{size:"large",type:"password",autocomplete:"true",placeholder:"密码"}},[a("a-icon",{style:{color:"rgba(0,0,0,.25)"},attrs:{slot:"prefix",type:"lock"},slot:"prefix"})],1)],1),a("a-form-item",{staticStyle:{"margin-top":"24px"}},[a("a-button",{staticClass:"login-button",attrs:{size:"large",type:"primary",htmlType:"submit",loading:t.state.loginBtn,disabled:t.state.loginBtn}},[t._v("\n 登陆\n ")])],1)],1)],1)},i=[],n=a("cebc"),o=a("2f62"),s=a("ca00"),l={name:"Login",data:function(){return{form:this.$form.createForm(this),state:{loginBtn:!1}}},methods:Object(n["a"])({},Object(o["b"])(["Login","Logout"]),{handleSubmit:function(t){var e=this;t.preventDefault(),this.form.validateFields(function(t,a){t?(e.state.loginBtn=!0,setTimeout(function(){e.state.loginBtn=!1},500)):e.Login(a).then(function(t){e.state.loginBtn=!0,e.loginSuccess(t)}).catch(function(t){e.loginFail(t),e.state.loginBtn=!1})})},loginSuccess:function(t){var e=this;this.$router.push({name:"project"}),setTimeout(function(){e.$notification.success({message:"欢迎",description:"".concat(Object(s["a"])()," ").concat(t.username,",欢迎回来")})},1e3)},loginFail:function(t){this.$notification.error({message:"错误",description:t,duration:4})}})},c=l,u=(a("698e"),a("2877")),m=Object(u["a"])(c,r,i,!1,null,"38784526",null);e["default"]=m.exports}}]);
//# sourceMappingURL=user.d638c020.js.map

File diff suppressed because one or more lines are too long

11
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es6",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"],
"include": ["src/**/*"]
}

16837
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

121
frontend/package.json Normal file
View File

@ -0,0 +1,121 @@
{
"name": "ConstructionConsultationSystem",
"version": "0.0.1",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"test:unit": "vue-cli-service test:unit"
},
"dependencies": {
"@antv/data-set": "^0.10.1",
"ant-design-vue": "~1.3.2",
"axios": "^0.18.0",
"enquire.js": "^2.1.6",
"js-cookie": "^2.2.0",
"lodash": "^4.17.11",
"lodash.get": "^4.4.2",
"lodash.pick": "^4.4.0",
"md5": "^2.2.1",
"moment": "^2.24.0",
"nprogress": "^0.2.0",
"viser-vue": "^2.3.3",
"vue": "^2.5.22",
"vue-clipboard2": "^0.2.1",
"vue-cropper": "^0.4.4",
"vue-ls": "^3.2.0",
"vue-router": "^3.0.1",
"vuex": "^3.1.0",
"whatwg-fetch": "^3.0.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.3.0",
"@vue/cli-plugin-eslint": "^3.3.0",
"@vue/cli-plugin-unit-jest": "^3.3.0",
"@vue/cli-service": "^3.5.3",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "^1.0.0-beta.20",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
"babel-plugin-import": "^1.11.0",
"eslint": "^5.8.0",
"eslint-plugin-html": "^5.0.0",
"eslint-plugin-vue": "^5.0.0",
"less": "^3.8.1",
"less-loader": "^4.1.0",
"vue-template-compiler": "^2.5.22"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/strongly-recommended",
"@vue/standard"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {
"generator-star-spacing": "off",
"no-mixed-operators": 0,
"vue/max-attributes-per-line": [
2,
{
"singleline": 5,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}
],
"vue/attribute-hyphenation": 0,
"vue/html-self-closing": 0,
"vue/component-name-in-template-casing": 0,
"vue/html-closing-bracket-spacing": 0,
"vue/singleline-html-element-content-newline": 0,
"vue/no-unused-components": 0,
"vue/multiline-html-element-content-newline": 0,
"vue/no-use-v-if-with-v-for": 0,
"vue/html-closing-bracket-newline": 0,
"vue/no-parsing-error": 0,
"no-console": 0,
"no-tabs": 0,
"quotes": [
2,
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"semi": [
2,
"never",
{
"beforeStatementContinuationChars": "never"
}
],
"no-delete-var": 2,
"prefer-const": [
2,
{
"ignoreReadBeforeAssign": false
}
]
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

7684
frontend/public/color.less vendored Normal file

File diff suppressed because it is too large Load Diff

20
frontend/public/index.html vendored Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>logo.png">
<title>Booking Admin</title>
<style>#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}</style>
</head>
<body>
<noscript>
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>
</div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -0,0 +1 @@
#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}

View File

@ -0,0 +1 @@
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>

View File

@ -0,0 +1,5 @@
<div class="preloading-animate">
<div class="preloading-wrapper">
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>
</div>
</div>

View File

@ -0,0 +1 @@
.preloading-animate{background:#ffffff;width:100%;height:100%;position:fixed;left:0;top:0;z-index:299;}.preloading-animate .preloading-wrapper{position:absolute;width:5rem;height:5rem;left:50%;top:50%;transform:translate(-50%,-50%);}.preloading-animate .preloading-wrapper .preloading-balls{font-size:5rem;}

View File

@ -0,0 +1 @@
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
frontend/public/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

45
frontend/src/App.vue Normal file
View File

@ -0,0 +1,45 @@
<template>
<a-locale-provider :locale="locale">
<div id="app">
<router-view/>
</div>
</a-locale-provider>
</template>
<script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import { deviceEnquire, DEVICE_TYPE } from '@/utils/device'
export default {
data () {
return {
locale: zhCN
}
},
mounted () {
const { $store } = this
deviceEnquire(deviceType => {
switch (deviceType) {
case DEVICE_TYPE.DESKTOP:
$store.commit('TOGGLE_DEVICE', 'desktop')
$store.dispatch('setSidebar', true)
break
case DEVICE_TYPE.TABLET:
$store.commit('TOGGLE_DEVICE', 'tablet')
$store.dispatch('setSidebar', false)
break
case DEVICE_TYPE.MOBILE:
default:
$store.commit('TOGGLE_DEVICE', 'mobile')
$store.dispatch('setSidebar', true)
break
}
})
}
}
</script>
<style>
#app {
height: 100%;
}
</style>

View File

@ -0,0 +1,25 @@
import fetchAPI from '../utils/fetch'
export default {
getRoleList () {
return fetchAPI('user/roleList/', 'get')
},
getUserList (params) {
return fetchAPI('user/', 'get', null, params)
},
createUser (user) {
return fetchAPI('user/', 'post', user)
},
getUser (id) {
return fetchAPI(`user/${id}/`, 'get')
},
updateUser (id, user) {
return fetchAPI(`user/${id}/`, 'put', user)
},
deleteUser (id) {
return fetchAPI(`user/${id}/`, 'delete')
},
checkUsername (username) {
return fetchAPI('user/checkUsername/', 'get', null, { username })
}
}

View File

@ -0,0 +1,13 @@
import fetchAPI from '../utils/fetch'
export default {
getBookingList (params) {
return fetchAPI('booking/', 'get', null, params)
},
createBooking (booking) {
return fetchAPI('booking/', 'post', booking)
},
getStatusList () {
return fetchAPI('booking/statusList/', 'get')
}
}

View File

@ -0,0 +1,10 @@
import fetchAPI from '../utils/fetch'
export default {
getRoomBookingStatus (params) {
return fetchAPI('booking/room/bookingStatus/', 'get', null, params)
},
getRoomBookingStatusDetail (id, params) {
return fetchAPI(`booking/room/${id}/bookingStatus/`, 'get', null, params)
}
}

0
frontend/src/api/help.js Normal file
View File

22
frontend/src/api/room.js Normal file
View File

@ -0,0 +1,22 @@
import fetchAPI from '../utils/fetch'
export default {
getRoomList (params) {
return fetchAPI('booking/room/', 'get', null, params)
},
getRoom (id) {
return fetchAPI(`booking/room/${id}/`, 'get')
},
updateRoom (id, room) {
return fetchAPI(`booking/room/${id}/`, 'put', room)
},
getSeatList (params) {
return fetchAPI('booking/seat/', 'get', null, params)
},
createSeat (seat) {
return fetchAPI('booking/seat/', 'post', seat)
},
deleteSeat (id) {
return fetchAPI(`booking/seat/${id}`, 'delete')
}
}

View File

@ -0,0 +1,10 @@
import fetchAPI from '../utils/fetch'
export default {
getSettingDetail() {
return fetchAPI('booking/setting/1/', 'get')
},
updateSettingDetail(setting) {
return fetchAPI('booking/setting/1/', 'put', setting)
}
}

21
frontend/src/api/user.js Normal file
View File

@ -0,0 +1,21 @@
import fetchAPI from '../utils/fetch'
export default {
login (username, password) {
return fetchAPI('user/login/', 'post', {
username,
password
})
},
logout () {
return fetchAPI('user/logout/', 'get')
},
getUser () {
return fetchAPI('user/getInfo/', 'get')
},
resetPassword (new_password) {
return fetchAPI('user/resetPassword/', 'post', {
new_password
})
}
}

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="200px" height="200px" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 47.1 (45422) - http://www.bohemiancoding.com/sketch -->
<title>Group 28 Copy 5</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="62.1023273%" y1="0%" x2="108.19718%" y2="37.8635764%" id="linearGradient-1">
<stop stop-color="#4285EB" offset="0%"></stop>
<stop stop-color="#2EC7FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="69.644116%" y1="0%" x2="54.0428975%" y2="108.456714%" id="linearGradient-2">
<stop stop-color="#29CDFF" offset="0%"></stop>
<stop stop-color="#148EFF" offset="37.8600687%"></stop>
<stop stop-color="#0A60FF" offset="100%"></stop>
</linearGradient>
<linearGradient x1="69.6908165%" y1="-12.9743587%" x2="16.7228981%" y2="117.391248%" id="linearGradient-3">
<stop stop-color="#FA816E" offset="0%"></stop>
<stop stop-color="#F74A5C" offset="41.472606%"></stop>
<stop stop-color="#F51D2C" offset="100%"></stop>
</linearGradient>
<linearGradient x1="68.1279872%" y1="-35.6905737%" x2="30.4400914%" y2="114.942679%" id="linearGradient-4">
<stop stop-color="#FA8E7D" offset="0%"></stop>
<stop stop-color="#F74A5C" offset="51.2635191%"></stop>
<stop stop-color="#F51D2C" offset="100%"></stop>
</linearGradient>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="logo" transform="translate(-20.000000, -20.000000)">
<g id="Group-28-Copy-5" transform="translate(20.000000, 20.000000)">
<g id="Group-27-Copy-3">
<g id="Group-25" fill-rule="nonzero">
<g id="2">
<path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C99.2571609,26.9692191 101.032305,26.9692191 102.20193,28.1378823 L129.985225,55.8983314 C134.193707,60.1033528 141.017005,60.1033528 145.225487,55.8983314 C149.433969,51.69331 149.433969,44.8756232 145.225487,40.6706018 L108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-1)"></path>
<path d="M91.5880863,4.17652823 L4.17996544,91.5127728 C-0.519240605,96.2081146 -0.519240605,103.791885 4.17996544,108.487227 L91.5880863,195.823472 C96.2872923,200.518814 103.877304,200.518814 108.57651,195.823472 L145.225487,159.204632 C149.433969,154.999611 149.433969,148.181924 145.225487,143.976903 C141.017005,139.771881 134.193707,139.771881 129.985225,143.976903 L102.20193,171.737352 C101.032305,172.906015 99.2571609,172.906015 98.0875359,171.737352 L28.285908,101.993122 C27.1162831,100.824459 27.1162831,99.050775 28.285908,97.8821118 L98.0875359,28.1378823 C100.999864,25.6271836 105.751642,20.541824 112.729652,19.3524487 C117.915585,18.4685261 123.585219,20.4140239 129.738554,25.1889424 C125.624663,21.0784292 118.571995,14.0340304 108.58055,4.05574592 C103.862049,-0.537986846 96.2692618,-0.500797906 91.5880863,4.17652823 Z" id="Shape" fill="url(#linearGradient-2)"></path>
</g>
<path d="M153.685633,135.854579 C157.894115,140.0596 164.717412,140.0596 168.925894,135.854579 L195.959977,108.842726 C200.659183,104.147384 200.659183,96.5636133 195.960527,91.8688194 L168.690777,64.7181159 C164.472332,60.5180858 157.646868,60.5241425 153.435895,64.7316526 C149.227413,68.936674 149.227413,75.7543607 153.435895,79.9593821 L171.854035,98.3623765 C173.02366,99.5310396 173.02366,101.304724 171.854035,102.473387 L153.685633,120.626849 C149.47715,124.83187 149.47715,131.649557 153.685633,135.854579 Z" id="Shape" fill="url(#linearGradient-3)"></path>
</g>
<ellipse id="Combined-Shape" fill="url(#linearGradient-4)" cx="100.519339" cy="100.436681" rx="23.6001926" ry="23.580786"></ellipse>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -0,0 +1,46 @@
<template>
<tooltip v-if="tips !== ''">
<template slot="title">{{ tips }}</template>
<avatar :size="avatarSize" :src="src" />
</tooltip>
<avatar v-else :size="avatarSize" :src="src" />
</template>
<script>
import Avatar from 'ant-design-vue/es/avatar'
import Tooltip from 'ant-design-vue/es/tooltip'
export default {
name: 'AvatarItem',
components: {
Avatar,
Tooltip
},
props: {
tips: {
type: String,
default: '',
required: false
},
src: {
type: String,
default: ''
}
},
data () {
return {
size: this.$parent.size
}
},
computed: {
avatarSize () {
return this.size !== 'mini' && this.size || 20
}
},
watch: {
'$parent.size' (val) {
this.size = val
}
}
}
</script>

View File

@ -0,0 +1,99 @@
<!--
<template>
<div :class="[prefixCls]">
<ul>
<slot></slot>
<template v-for="item in filterEmpty($slots.default).slice(0, 3)"></template>
<template v-if="maxLength > 0 && filterEmpty($slots.default).length > maxLength">
<avatar-item :size="size">
<avatar :size="size !== 'mini' && size || 20" :style="excessItemsStyle">{{ `+${maxLength}` }}</avatar>
</avatar-item>
</template>
</ul>
</div>
</template>
-->
<script>
import Avatar from 'ant-design-vue/es/avatar'
import AvatarItem from './Item'
import { filterEmpty } from '@/components/_util/util'
export default {
AvatarItem,
name: 'AvatarList',
components: {
Avatar,
AvatarItem
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-avatar-list'
},
/**
* 头像大小 类型: largesmall mini, default
* 默认值: default
*/
size: {
type: [String, Number],
default: 'default'
},
/**
* 要显示的最大项目
*/
maxLength: {
type: Number,
default: 0
},
/**
* 多余的项目风格
*/
excessItemsStyle: {
type: Object,
default: () => {
return {
color: '#f56a00',
backgroundColor: '#fde3cf'
}
}
}
},
data () {
return {}
},
methods: {
getItems (items) {
const classString = {
[`${this.prefixCls}-item`]: true,
[`${this.size}`]: true
}
if (this.maxLength > 0) {
items = items.slice(0, this.maxLength)
items.push((<Avatar size={ this.size } style={ this.excessItemsStyle }>{`+${this.maxLength}`}</Avatar>))
}
const itemList = items.map((item) => (
<li class={ classString }>{ item }</li>
))
return itemList
}
},
render () {
const { prefixCls, size } = this.$props
const classString = {
[`${prefixCls}`]: true,
[`${size}`]: true
}
const items = filterEmpty(this.$slots.default)
const itemsDom = items && items.length ? <ul class={`${prefixCls}-items`}>{ this.getItems(items) }</ul> : null
return (
<div class={ classString }>
{ itemsDom }
</div>
)
}
}
</script>

View File

@ -0,0 +1,4 @@
import AvatarList from './List'
import './index.less'
export default AvatarList

View File

@ -0,0 +1,60 @@
@import "../index";
@avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list";
@avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item";
.@{avatar-list-prefix-cls} {
display: inline-block;
ul {
list-style: none;
display: inline-block;
padding: 0;
margin: 0 0 0 8px;
font-size: 0;
}
}
.@{avatar-list-item-prefix-cls} {
display: inline-block;
font-size: @font-size-base;
margin-left: -8px;
width: @avatar-size-base;
height: @avatar-size-base;
:global {
.ant-avatar {
border: 1px solid #fff;
cursor: pointer;
}
}
&.large {
width: @avatar-size-lg;
height: @avatar-size-lg;
}
&.small {
width: @avatar-size-sm;
height: @avatar-size-sm;
}
&.mini {
width: 20px;
height: 20px;
:global {
.ant-avatar {
width: 20px;
height: 20px;
line-height: 20px;
.ant-avatar-string {
font-size: 12px;
line-height: 18px;
}
}
}
}
}

View File

@ -0,0 +1,64 @@
# AvatarList 用户头像列表
一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。
引用方式:
```javascript
import AvatarList from '@/components/AvatarList'
const AvatarListItem = AvatarList.AvatarItem
export default {
components: {
AvatarList,
AvatarListItem
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
<avatar-list size="mini">
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
```
```html
<avatar-list :max-length="3">
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
```
## API
### AvatarList
| 参数 | 说明 | 类型 | 默认值 |
| ---------------- | -------- | ---------------------------------- | --------- |
| size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` |
| maxLength | 要显示的最大项目 | number | - |
| excessItemsStyle | 多余的项目风格 | CSSProperties | - |
### AvatarList.Item
| 参数 | 说明 | 类型 | 默认值 |
| ---- | ------ | --------- | --- |
| tips | 头像展示文案 | string | - |
| src | 头像图片连接 | string | - |

View File

@ -0,0 +1,102 @@
<template>
<span>
{{ lastTime | format }}
</span>
</template>
<script>
function fixedZero (val) {
return val * 1 < 10 ? `0${val}` : val
}
export default {
name: 'CountDown',
props: {
format: {
type: Function,
default: undefined
},
target: {
type: [Date, Number],
required: true
},
onEnd: {
type: Function,
default: () => ({})
}
},
data () {
return {
dateTime: '0',
originTargetTime: 0,
lastTime: 0,
timer: 0,
interval: 1000
}
},
filters: {
format (time) {
const hours = 60 * 60 * 1000
const minutes = 60 * 1000
const h = Math.floor(time / hours)
const m = Math.floor((time - h * hours) / minutes)
const s = Math.floor((time - h * hours - m * minutes) / 1000)
return `${fixedZero(h)}:${fixedZero(m)}:${fixedZero(s)}`
}
},
created () {
this.initTime()
this.tick()
},
methods: {
initTime () {
let lastTime = 0
let targetTime = 0
this.originTargetTime = this.target
try {
if (Object.prototype.toString.call(this.target) === '[object Date]') {
targetTime = this.target
} else {
targetTime = new Date(this.target).getTime()
}
} catch (e) {
throw new Error('invalid target prop')
}
lastTime = targetTime - new Date().getTime()
this.lastTime = lastTime < 0 ? 0 : lastTime
},
tick () {
const { onEnd } = this
this.timer = setTimeout(() => {
if (this.lastTime < this.interval) {
clearTimeout(this.timer)
this.lastTime = 0
if (typeof onEnd === 'function') {
onEnd()
}
} else {
this.lastTime -= this.interval
this.tick()
}
}, this.interval)
}
},
beforeUpdate () {
if (this.originTargetTime !== this.target) {
this.initTime()
}
},
beforeDestroy () {
clearTimeout(this.timer)
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,3 @@
import CountDown from './CountDown'
export default CountDown

View File

@ -0,0 +1,34 @@
# CountDown 倒计时
倒计时组件。
引用方式:
```javascript
import CountDown from '@/components/CountDown/CountDown'
export default {
components: {
CountDown
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
<count-down :target="new Date().getTime() + 3000000" :on-end="onEndHandle" />
```
## API
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| target | 目标时间 | Date | - |
| onEnd | 倒计时结束回调 | funtion | -|

View File

@ -0,0 +1,64 @@
<script>
import Tooltip from 'ant-design-vue/es/tooltip'
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/StringUtil'
/*
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
const TooltipOverlayStyle = {
overflowWrap: 'break-word',
wordWrap: 'break-word',
};
*/
export default {
name: 'Ellipsis',
components: {
Tooltip
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-ellipsis'
},
tooltip: {
type: Boolean
},
length: {
type: Number,
required: true
},
lines: {
type: Number,
default: 1
},
fullWidthRecognition: {
type: Boolean,
default: false
}
},
methods: {
getStrDom (str, fullLength) {
return (
<span>{ cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '') }</span>
)
},
getTooltip (fullStr, fullLength) {
return (
<Tooltip>
<template slot="title">{ fullStr }</template>
{ this.getStrDom(fullStr, fullLength) }
</Tooltip>
)
}
},
render () {
const { tooltip, length } = this.$props
const str = this.$slots.default.map(vNode => vNode.text).join('')
const fullLength = getStrFullLength(str)
const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
return (
strDom
)
}
}
</script>

View File

@ -0,0 +1,3 @@
import Ellipsis from './Ellipsis'
export default Ellipsis

View File

@ -0,0 +1,38 @@
# Ellipsis 文本自动省略号
文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。
引用方式:
```javascript
import Ellipsis from '@/components/Ellipsis'
export default {
components: {
Ellipsis
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
<ellipsis :length="100" tooltip>
There were injuries alleged in three cases in 2015, and a
fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.
</ellipsis>
```
## API
参数 | 说明 | 类型 | 默认值
----|------|-----|------
tooltip | 移动到文本展示完整内容的提示 | boolean | -
length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | -

View File

@ -0,0 +1,30 @@
<template>
<div :class="prefixCls">
<div style="float: left">
<slot name="extra">{{ extra }}</slot>
</div>
<div style="float: right">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: 'FooterToolBar',
props: {
prefixCls: {
type: String,
default: 'ant-pro-footer-toolbar'
},
extra: {
type: [String, Object],
default: ''
}
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,4 @@
import FooterToolBar from './FooterToolBar'
import './index.less'
export default FooterToolBar

View File

@ -0,0 +1,23 @@
@import "../index";
@footer-toolbar-prefix-cls: ~"@{ant-pro-prefix}-footer-toolbar";
.@{footer-toolbar-prefix-cls} {
position: fixed;
width: 100%;
bottom: 0;
right: 0;
height: 56px;
line-height: 56px;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03);
background: #fff;
border-top: 1px solid #e8e8e8;
padding: 0 24px;
z-index: 9;
&:after {
content: "";
display: block;
clear: both;
}
}

View File

@ -0,0 +1,48 @@
# FooterToolbar 底部工具栏
固定在底部的工具栏。
## 何时使用
固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。
引用方式:
```javascript
import FooterToolBar from '@/components/FooterToolbar'
export default {
components: {
FooterToolBar
}
}
```
## 代码演示
```html
<footer-tool-bar>
<a-button type="primary" @click="validate" :loading="loading">提交</a-button>
</footer-tool-bar>
```
```html
<footer-tool-bar extra="扩展信息提示">
<a-button type="primary" @click="validate" :loading="loading">提交</a-button>
</footer-tool-bar>
```
## API
参数 | 说明 | 类型 | 默认值
----|------|-----|------
children (slot) | 工具栏内容,向右对齐 | - | -
extra | 额外信息,向左对齐 | String, Object | -

View File

@ -0,0 +1,56 @@
<template>
<div style="margin: -23px -24px 24px -24px">
<a-tabs
hideAdd
v-model="activeKey"
type="editable-card"
:tabBarStyle="{ background: '#FFF', margin: 0, paddingLeft: '16px', paddingTop: '1px' }"
@edit="onEdit"
>
<a-tab-pane v-for="page in pages" :style="{ height: 0 }" :tab="page.meta.title" :key="page.fullPath" :closable="pages.length > 1">
</a-tab-pane>
</a-tabs>
</div>
</template>
<script>
export default {
name: 'MultiTab',
data () {
return {
fullPathList: [],
pages: [],
activeKey: '',
newTabIndex: 0
}
},
created () {
this.pages.push(this.$route)
this.fullPathList.push(this.$route.fullPath)
},
methods: {
onEdit (targetKey, action) {
this[action](targetKey)
},
remove (targetKey) {
if (this.pages.length === 1) {
return
}
this.pages = this.pages.filter(page => page.fullPath !== targetKey)
this.fullPathList = this.fullPathList.filter(path => path !== targetKey)
}
},
watch: {
'$route': function (newVal) {
this.activeKey = newVal.fullPath
if (this.fullPathList.indexOf(newVal.fullPath) < 0) {
this.fullPathList.push(newVal.fullPath)
this.pages.push(newVal)
}
},
activeKey: function (newPathKey) {
this.$router.push({ path: newPathKey })
}
}
}
</script>

Some files were not shown because too many files have changed in this diff Show More