add case-edit-module

This commit is contained in:
烧煤的快感
2017-11-22 19:07:48 +08:00
parent e3b04aab2f
commit 089ca8b4a5
52 changed files with 13524 additions and 7298 deletions
+3
View File
@@ -9,6 +9,9 @@ sys.path.insert(0, BASE_DIR)
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps'))
# 此程序的请求URL
REQUEST_HOST = 'http://127.0.0.1:8000'
# 此密钥将会在REST-Framework-JWT中作为JWT_SECRET_KEY
SECRET_KEY = 'y0vj*z2@3@53nht9t!hn4jac*$6oufv+w24r65*4vom1a^2cca'
+3 -4
View File
@@ -10,10 +10,10 @@ from rest_framework.routers import DefaultRouter
from rest_framework.authtoken import views
from rest_framework_jwt.views import obtain_jwt_token
from cases.views import CasesListViewSet, CategoryViewset, HotSearchsViewset, BannerViewset
from cases.views import IndexCategoryViewset
from users.views import SmsCodeViewset, UserViewset, PictureCodeView
from user_operation.views import UserFavViewset, LeavingMessageViewset
from user_operation.views import UserFavViewset
from django.views.generic import TemplateView
from utils.file_views import UploadFileView
router = DefaultRouter() # 利用了ViewSet的重载as_view()方法进行请求方法和处理函数的动态绑定(简单好用,只需要逐一register,然后urls函数即可)
router.register(r'cases', CasesListViewSet, base_name="cases") # 案例列表的相关操作
@@ -22,11 +22,10 @@ router.register(r'smscodes', SmsCodeViewset, base_name="smscodes") # 短信
router.register(r'hotsearchs', HotSearchsViewset, base_name="hotsearchs") # 热搜案例的相关操作
router.register(r'users', UserViewset, base_name="users") # 用户的相关操作
router.register(r'userfavs', UserFavViewset, base_name="userfavs") # 用户收藏的相关操作
router.register(r'messages', LeavingMessageViewset, base_name="messages") # 用户留言的相关操作
router.register(r'banners', BannerViewset, base_name="banners") # 轮播图的相关操作
router.register(r'indexcases', IndexCategoryViewset, base_name="indexcases") # 首页案例的相关操作
urlpatterns = [
url(r'^uploadfile/(.+)/$', UploadFileView.as_view()), # 上传文件接口
url(r'^picturecodes/', PictureCodeView.as_view(), name='picturecodes'), # 获取验证码图片
url(r'^', include(router.urls)), # 将调用刚才注册到router的各个ViewSet的as_view()方法,得到最终的URL映射配置
url(r'^xadmin/', xadmin.site.urls), # 注册xadmin后台管理平台的URL处理函数
+27
View File
@@ -0,0 +1,27 @@
# BootStrap笔记
## 1.响应式分界点
①xs<576pxExtra smallportrait phones
②sm576px--768pxSmalllandscape phones
③md768px--992pxMediumtablets
④lg992px--1200pxLargedesktops
⑤xl>1200pxExtra largelarge desktops
实现方式为:
```
media (min-width: 576px) { ... }
```
或者
```
include media-breakpoint-up(sm) { ... }
```
即基于宽度的最小值,意味着它们应用到某一等级以及该等级之上的所有等级(比如:`.col-sm-4`应用到小、中、大、特大设备)
+21 -10
View File
@@ -1,22 +1,33 @@
<!doctype html>
<html class="no-js" lang="zh-CN">
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta charset="utf-8">
<!-- 在IE运行最新的渲染模式 -->
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- 初始化移动浏览显示 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>APP Inventor案例库网站</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css"/>
<!-- 加载最新版本的字体图标库font-awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div id="app" v-cloak>
<router-view></router-view>
</div>
<!-- Bootstrap依赖于jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/what-input/5.0.3/what-input.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- 基于Bootstrap3的HTML5表单验证库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
</body>
</html>
@@ -8,6 +8,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>APP Inventor案例库网站</title>
<!-- 加载最新版本的字体图标库font-awesome -->
<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
-102
View File
@@ -1,102 +0,0 @@
.menu-fancy li {
position: relative;
margin: 0 5px;
}
.menu-fancy li a {
padding: 0.5rem 0;
color: #8a8a8a;
}
.menu-fancy li:after,
.menu-fancy li:before,
.menu-fancy a:before,
.menu-fancy a:after {
position: absolute;
content: '';
border-radius: 4px;
}
.menu-fancy li:after,
.menu-fancy li:before {
bottom: -4px;
height: 4px;
}
.menu-fancy li:not(.is-selected):before {
left: 0;
right: -2px;
}
.menu-fancy li.is-selected:after {
left: 0;
right: 39px;
}
.menu-fancy li.is-selected:before {
right: -2px;
width: 13px;
}
.menu-fancy .is-selected a:before {
height: 22px;
width: 4px;
bottom: -22px;
right: 7px;
}
.menu-fancy .is-selected a:after {
height: 4px;
width: 40px;
bottom: -13px;
right: 5px;
-webkit-transform: rotate(30deg);
-ms-transform: rotate(30deg);
transform: rotate(30deg);
}
.menu-fancy .menu-fancy-option1 a:hover {
color: #3adb76;
}
.menu-fancy .menu-fancy-option1:after,
.menu-fancy .menu-fancy-option1:before,
.menu-fancy .menu-fancy-option1 a:after,
.menu-fancy .menu-fancy-option1 a:before {
background-color: #3adb76;
}
.menu-fancy .menu-fancy-option2 a:hover {
color: #cc4b37;
}
.menu-fancy .menu-fancy-option2:after,
.menu-fancy .menu-fancy-option2:before,
.menu-fancy .menu-fancy-option2 a:after,
.menu-fancy .menu-fancy-option2 a:before {
background-color: #cc4b37;
}
.menu-fancy .menu-fancy-option3 a:hover {
color: #1779ba;
}
.menu-fancy .menu-fancy-option3:after,
.menu-fancy .menu-fancy-option3:before,
.menu-fancy .menu-fancy-option3 a:after,
.menu-fancy .menu-fancy-option3 a:before {
background-color: #1779ba;
}
.menu-fancy .menu-fancy-option4 a:hover {
color: #ffae00;
}
.menu-fancy .menu-fancy-option4:after,
.menu-fancy .menu-fancy-option4:before,
.menu-fancy .menu-fancy-option4 a:after,
.menu-fancy .menu-fancy-option4 a:before {
background-color: #ffae00;
}
+6 -14
View File
@@ -7,26 +7,18 @@
<!-- 初始化移动浏览显示 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>APP Inventor案例库网站</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css"/>
<link rel="stylesheet" href="./index_foundation.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/motion-ui/1.2.3/motion-ui.min.css"/>
</head>
<body>
<ul class="menu menu-fancy">
<li class="menu-fancy-option1 is-selected"><a href="#">About Me</a></li>
<li class="menu-fancy-option2"><a href="#">Work</a></li>
<li class="menu-fancy-option3"><a href="#">Resume</a></li>
<li class="menu-fancy-option4"><a href="#">Contact</a>
</li>
</ul>
<div id="app" v-cloak>
<router-view></router-view>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/what-input/5.0.3/what-input.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/motion-ui/1.2.3/motion-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js"></script>
<script>
$(document).foundation();
</script>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
var str = '令错误';
var index = str.indexOf('令牌');
console.log(index);
+2
View File
@@ -33,6 +33,8 @@
"babel-preset-env": "^1.2.1",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"bootstrap": "^4.0.0-beta.2",
"bootstrap-validator": "^0.11.9",
"chalk": "^1.1.3",
"connect-history-api-fallback": "^1.1.0",
"css-loader": "^0.27.3",
File diff suppressed because it is too large Load Diff
@@ -1,594 +0,0 @@
/*
* Foundation Icons v 3.0
* Made by ZURB 2013 http://zurb.com/playground/foundation-icon-fonts-3
* MIT License
*/
@font-face {
font-family: "foundation-icons";
src: url("foundation-icons.eot");
src: url("foundation-icons.eot?#iefix") format("embedded-opentype"),
url("foundation-icons.woff") format("woff"),
url("foundation-icons.ttf") format("truetype"),
url("foundation-icons.svg#fontcustom") format("svg");
font-weight: normal;
font-style: normal;
}
.fi-address-book:before,
.fi-alert:before,
.fi-align-center:before,
.fi-align-justify:before,
.fi-align-left:before,
.fi-align-right:before,
.fi-anchor:before,
.fi-annotate:before,
.fi-archive:before,
.fi-arrow-down:before,
.fi-arrow-left:before,
.fi-arrow-right:before,
.fi-arrow-up:before,
.fi-arrows-compress:before,
.fi-arrows-expand:before,
.fi-arrows-in:before,
.fi-arrows-out:before,
.fi-asl:before,
.fi-asterisk:before,
.fi-at-sign:before,
.fi-background-color:before,
.fi-battery-empty:before,
.fi-battery-full:before,
.fi-battery-half:before,
.fi-bitcoin-circle:before,
.fi-bitcoin:before,
.fi-blind:before,
.fi-bluetooth:before,
.fi-bold:before,
.fi-book-bookmark:before,
.fi-book:before,
.fi-bookmark:before,
.fi-braille:before,
.fi-burst-new:before,
.fi-burst-sale:before,
.fi-burst:before,
.fi-calendar:before,
.fi-camera:before,
.fi-check:before,
.fi-checkbox:before,
.fi-clipboard-notes:before,
.fi-clipboard-pencil:before,
.fi-clipboard:before,
.fi-clock:before,
.fi-closed-caption:before,
.fi-cloud:before,
.fi-comment-minus:before,
.fi-comment-quotes:before,
.fi-comment-video:before,
.fi-comment:before,
.fi-comments:before,
.fi-compass:before,
.fi-contrast:before,
.fi-credit-card:before,
.fi-crop:before,
.fi-crown:before,
.fi-css3:before,
.fi-database:before,
.fi-die-five:before,
.fi-die-four:before,
.fi-die-one:before,
.fi-die-six:before,
.fi-die-three:before,
.fi-die-two:before,
.fi-dislike:before,
.fi-dollar-bill:before,
.fi-dollar:before,
.fi-download:before,
.fi-eject:before,
.fi-elevator:before,
.fi-euro:before,
.fi-eye:before,
.fi-fast-forward:before,
.fi-female-symbol:before,
.fi-female:before,
.fi-filter:before,
.fi-first-aid:before,
.fi-flag:before,
.fi-folder-add:before,
.fi-folder-lock:before,
.fi-folder:before,
.fi-foot:before,
.fi-foundation:before,
.fi-graph-bar:before,
.fi-graph-horizontal:before,
.fi-graph-pie:before,
.fi-graph-trend:before,
.fi-guide-dog:before,
.fi-hearing-aid:before,
.fi-heart:before,
.fi-home:before,
.fi-html5:before,
.fi-indent-less:before,
.fi-indent-more:before,
.fi-info:before,
.fi-italic:before,
.fi-key:before,
.fi-laptop:before,
.fi-layout:before,
.fi-lightbulb:before,
.fi-like:before,
.fi-link:before,
.fi-list-bullet:before,
.fi-list-number:before,
.fi-list-thumbnails:before,
.fi-list:before,
.fi-lock:before,
.fi-loop:before,
.fi-magnifying-glass:before,
.fi-mail:before,
.fi-male-female:before,
.fi-male-symbol:before,
.fi-male:before,
.fi-map:before,
.fi-marker:before,
.fi-megaphone:before,
.fi-microphone:before,
.fi-minus-circle:before,
.fi-minus:before,
.fi-mobile-signal:before,
.fi-mobile:before,
.fi-monitor:before,
.fi-mountains:before,
.fi-music:before,
.fi-next:before,
.fi-no-dogs:before,
.fi-no-smoking:before,
.fi-page-add:before,
.fi-page-copy:before,
.fi-page-csv:before,
.fi-page-delete:before,
.fi-page-doc:before,
.fi-page-edit:before,
.fi-page-export-csv:before,
.fi-page-export-doc:before,
.fi-page-export-pdf:before,
.fi-page-export:before,
.fi-page-filled:before,
.fi-page-multiple:before,
.fi-page-pdf:before,
.fi-page-remove:before,
.fi-page-search:before,
.fi-page:before,
.fi-paint-bucket:before,
.fi-paperclip:before,
.fi-pause:before,
.fi-paw:before,
.fi-paypal:before,
.fi-pencil:before,
.fi-photo:before,
.fi-play-circle:before,
.fi-play-video:before,
.fi-play:before,
.fi-plus:before,
.fi-pound:before,
.fi-power:before,
.fi-previous:before,
.fi-price-tag:before,
.fi-pricetag-multiple:before,
.fi-print:before,
.fi-prohibited:before,
.fi-projection-screen:before,
.fi-puzzle:before,
.fi-quote:before,
.fi-record:before,
.fi-refresh:before,
.fi-results-demographics:before,
.fi-results:before,
.fi-rewind-ten:before,
.fi-rewind:before,
.fi-rss:before,
.fi-safety-cone:before,
.fi-save:before,
.fi-share:before,
.fi-sheriff-badge:before,
.fi-shield:before,
.fi-shopping-bag:before,
.fi-shopping-cart:before,
.fi-shuffle:before,
.fi-skull:before,
.fi-social-500px:before,
.fi-social-adobe:before,
.fi-social-amazon:before,
.fi-social-android:before,
.fi-social-apple:before,
.fi-social-behance:before,
.fi-social-bing:before,
.fi-social-blogger:before,
.fi-social-delicious:before,
.fi-social-designer-news:before,
.fi-social-deviant-art:before,
.fi-social-digg:before,
.fi-social-dribbble:before,
.fi-social-drive:before,
.fi-social-dropbox:before,
.fi-social-evernote:before,
.fi-social-facebook:before,
.fi-social-flickr:before,
.fi-social-forrst:before,
.fi-social-foursquare:before,
.fi-social-game-center:before,
.fi-social-github:before,
.fi-social-google-plus:before,
.fi-social-hacker-news:before,
.fi-social-hi5:before,
.fi-social-instagram:before,
.fi-social-joomla:before,
.fi-social-lastfm:before,
.fi-social-linkedin:before,
.fi-social-medium:before,
.fi-social-myspace:before,
.fi-social-orkut:before,
.fi-social-path:before,
.fi-social-picasa:before,
.fi-social-pinterest:before,
.fi-social-rdio:before,
.fi-social-reddit:before,
.fi-social-skillshare:before,
.fi-social-skype:before,
.fi-social-smashing-mag:before,
.fi-social-snapchat:before,
.fi-social-spotify:before,
.fi-social-squidoo:before,
.fi-social-stack-overflow:before,
.fi-social-steam:before,
.fi-social-stumbleupon:before,
.fi-social-treehouse:before,
.fi-social-tumblr:before,
.fi-social-twitter:before,
.fi-social-vimeo:before,
.fi-social-windows:before,
.fi-social-xbox:before,
.fi-social-yahoo:before,
.fi-social-yelp:before,
.fi-social-youtube:before,
.fi-social-zerply:before,
.fi-social-zurb:before,
.fi-sound:before,
.fi-star:before,
.fi-stop:before,
.fi-strikethrough:before,
.fi-subscript:before,
.fi-superscript:before,
.fi-tablet-landscape:before,
.fi-tablet-portrait:before,
.fi-target-two:before,
.fi-target:before,
.fi-telephone-accessible:before,
.fi-telephone:before,
.fi-text-color:before,
.fi-thumbnails:before,
.fi-ticket:before,
.fi-torso-business:before,
.fi-torso-female:before,
.fi-torso:before,
.fi-torsos-all-female:before,
.fi-torsos-all:before,
.fi-torsos-female-male:before,
.fi-torsos-male-female:before,
.fi-torsos:before,
.fi-trash:before,
.fi-trees:before,
.fi-trophy:before,
.fi-underline:before,
.fi-universal-access:before,
.fi-unlink:before,
.fi-unlock:before,
.fi-upload-cloud:before,
.fi-upload:before,
.fi-usb:before,
.fi-video:before,
.fi-volume-none:before,
.fi-volume-strike:before,
.fi-volume:before,
.fi-web:before,
.fi-wheelchair:before,
.fi-widget:before,
.fi-wrench:before,
.fi-x-circle:before,
.fi-x:before,
.fi-yen:before,
.fi-zoom-in:before,
.fi-zoom-out:before {
font-family: "foundation-icons";
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
display: inline-block;
text-decoration: inherit;
}
.fi-address-book:before { content: "\f100"; }
.fi-alert:before { content: "\f101"; }
.fi-align-center:before { content: "\f102"; }
.fi-align-justify:before { content: "\f103"; }
.fi-align-left:before { content: "\f104"; }
.fi-align-right:before { content: "\f105"; }
.fi-anchor:before { content: "\f106"; }
.fi-annotate:before { content: "\f107"; }
.fi-archive:before { content: "\f108"; }
.fi-arrow-down:before { content: "\f109"; }
.fi-arrow-left:before { content: "\f10a"; }
.fi-arrow-right:before { content: "\f10b"; }
.fi-arrow-up:before { content: "\f10c"; }
.fi-arrows-compress:before { content: "\f10d"; }
.fi-arrows-expand:before { content: "\f10e"; }
.fi-arrows-in:before { content: "\f10f"; }
.fi-arrows-out:before { content: "\f110"; }
.fi-asl:before { content: "\f111"; }
.fi-asterisk:before { content: "\f112"; }
.fi-at-sign:before { content: "\f113"; }
.fi-background-color:before { content: "\f114"; }
.fi-battery-empty:before { content: "\f115"; }
.fi-battery-full:before { content: "\f116"; }
.fi-battery-half:before { content: "\f117"; }
.fi-bitcoin-circle:before { content: "\f118"; }
.fi-bitcoin:before { content: "\f119"; }
.fi-blind:before { content: "\f11a"; }
.fi-bluetooth:before { content: "\f11b"; }
.fi-bold:before { content: "\f11c"; }
.fi-book-bookmark:before { content: "\f11d"; }
.fi-book:before { content: "\f11e"; }
.fi-bookmark:before { content: "\f11f"; }
.fi-braille:before { content: "\f120"; }
.fi-burst-new:before { content: "\f121"; }
.fi-burst-sale:before { content: "\f122"; }
.fi-burst:before { content: "\f123"; }
.fi-calendar:before { content: "\f124"; }
.fi-camera:before { content: "\f125"; }
.fi-check:before { content: "\f126"; }
.fi-checkbox:before { content: "\f127"; }
.fi-clipboard-notes:before { content: "\f128"; }
.fi-clipboard-pencil:before { content: "\f129"; }
.fi-clipboard:before { content: "\f12a"; }
.fi-clock:before { content: "\f12b"; }
.fi-closed-caption:before { content: "\f12c"; }
.fi-cloud:before { content: "\f12d"; }
.fi-comment-minus:before { content: "\f12e"; }
.fi-comment-quotes:before { content: "\f12f"; }
.fi-comment-video:before { content: "\f130"; }
.fi-comment:before { content: "\f131"; }
.fi-comments:before { content: "\f132"; }
.fi-compass:before { content: "\f133"; }
.fi-contrast:before { content: "\f134"; }
.fi-credit-card:before { content: "\f135"; }
.fi-crop:before { content: "\f136"; }
.fi-crown:before { content: "\f137"; }
.fi-css3:before { content: "\f138"; }
.fi-database:before { content: "\f139"; }
.fi-die-five:before { content: "\f13a"; }
.fi-die-four:before { content: "\f13b"; }
.fi-die-one:before { content: "\f13c"; }
.fi-die-six:before { content: "\f13d"; }
.fi-die-three:before { content: "\f13e"; }
.fi-die-two:before { content: "\f13f"; }
.fi-dislike:before { content: "\f140"; }
.fi-dollar-bill:before { content: "\f141"; }
.fi-dollar:before { content: "\f142"; }
.fi-download:before { content: "\f143"; }
.fi-eject:before { content: "\f144"; }
.fi-elevator:before { content: "\f145"; }
.fi-euro:before { content: "\f146"; }
.fi-eye:before { content: "\f147"; }
.fi-fast-forward:before { content: "\f148"; }
.fi-female-symbol:before { content: "\f149"; }
.fi-female:before { content: "\f14a"; }
.fi-filter:before { content: "\f14b"; }
.fi-first-aid:before { content: "\f14c"; }
.fi-flag:before { content: "\f14d"; }
.fi-folder-add:before { content: "\f14e"; }
.fi-folder-lock:before { content: "\f14f"; }
.fi-folder:before { content: "\f150"; }
.fi-foot:before { content: "\f151"; }
.fi-foundation:before { content: "\f152"; }
.fi-graph-bar:before { content: "\f153"; }
.fi-graph-horizontal:before { content: "\f154"; }
.fi-graph-pie:before { content: "\f155"; }
.fi-graph-trend:before { content: "\f156"; }
.fi-guide-dog:before { content: "\f157"; }
.fi-hearing-aid:before { content: "\f158"; }
.fi-heart:before { content: "\f159"; }
.fi-home:before { content: "\f15a"; }
.fi-html5:before { content: "\f15b"; }
.fi-indent-less:before { content: "\f15c"; }
.fi-indent-more:before { content: "\f15d"; }
.fi-info:before { content: "\f15e"; }
.fi-italic:before { content: "\f15f"; }
.fi-key:before { content: "\f160"; }
.fi-laptop:before { content: "\f161"; }
.fi-layout:before { content: "\f162"; }
.fi-lightbulb:before { content: "\f163"; }
.fi-like:before { content: "\f164"; }
.fi-link:before { content: "\f165"; }
.fi-list-bullet:before { content: "\f166"; }
.fi-list-number:before { content: "\f167"; }
.fi-list-thumbnails:before { content: "\f168"; }
.fi-list:before { content: "\f169"; }
.fi-lock:before { content: "\f16a"; }
.fi-loop:before { content: "\f16b"; }
.fi-magnifying-glass:before { content: "\f16c"; }
.fi-mail:before { content: "\f16d"; }
.fi-male-female:before { content: "\f16e"; }
.fi-male-symbol:before { content: "\f16f"; }
.fi-male:before { content: "\f170"; }
.fi-map:before { content: "\f171"; }
.fi-marker:before { content: "\f172"; }
.fi-megaphone:before { content: "\f173"; }
.fi-microphone:before { content: "\f174"; }
.fi-minus-circle:before { content: "\f175"; }
.fi-minus:before { content: "\f176"; }
.fi-mobile-signal:before { content: "\f177"; }
.fi-mobile:before { content: "\f178"; }
.fi-monitor:before { content: "\f179"; }
.fi-mountains:before { content: "\f17a"; }
.fi-music:before { content: "\f17b"; }
.fi-next:before { content: "\f17c"; }
.fi-no-dogs:before { content: "\f17d"; }
.fi-no-smoking:before { content: "\f17e"; }
.fi-page-add:before { content: "\f17f"; }
.fi-page-copy:before { content: "\f180"; }
.fi-page-csv:before { content: "\f181"; }
.fi-page-delete:before { content: "\f182"; }
.fi-page-doc:before { content: "\f183"; }
.fi-page-edit:before { content: "\f184"; }
.fi-page-export-csv:before { content: "\f185"; }
.fi-page-export-doc:before { content: "\f186"; }
.fi-page-export-pdf:before { content: "\f187"; }
.fi-page-export:before { content: "\f188"; }
.fi-page-filled:before { content: "\f189"; }
.fi-page-multiple:before { content: "\f18a"; }
.fi-page-pdf:before { content: "\f18b"; }
.fi-page-remove:before { content: "\f18c"; }
.fi-page-search:before { content: "\f18d"; }
.fi-page:before { content: "\f18e"; }
.fi-paint-bucket:before { content: "\f18f"; }
.fi-paperclip:before { content: "\f190"; }
.fi-pause:before { content: "\f191"; }
.fi-paw:before { content: "\f192"; }
.fi-paypal:before { content: "\f193"; }
.fi-pencil:before { content: "\f194"; }
.fi-photo:before { content: "\f195"; }
.fi-play-circle:before { content: "\f196"; }
.fi-play-video:before { content: "\f197"; }
.fi-play:before { content: "\f198"; }
.fi-plus:before { content: "\f199"; }
.fi-pound:before { content: "\f19a"; }
.fi-power:before { content: "\f19b"; }
.fi-previous:before { content: "\f19c"; }
.fi-price-tag:before { content: "\f19d"; }
.fi-pricetag-multiple:before { content: "\f19e"; }
.fi-print:before { content: "\f19f"; }
.fi-prohibited:before { content: "\f1a0"; }
.fi-projection-screen:before { content: "\f1a1"; }
.fi-puzzle:before { content: "\f1a2"; }
.fi-quote:before { content: "\f1a3"; }
.fi-record:before { content: "\f1a4"; }
.fi-refresh:before { content: "\f1a5"; }
.fi-results-demographics:before { content: "\f1a6"; }
.fi-results:before { content: "\f1a7"; }
.fi-rewind-ten:before { content: "\f1a8"; }
.fi-rewind:before { content: "\f1a9"; }
.fi-rss:before { content: "\f1aa"; }
.fi-safety-cone:before { content: "\f1ab"; }
.fi-save:before { content: "\f1ac"; }
.fi-share:before { content: "\f1ad"; }
.fi-sheriff-badge:before { content: "\f1ae"; }
.fi-shield:before { content: "\f1af"; }
.fi-shopping-bag:before { content: "\f1b0"; }
.fi-shopping-cart:before { content: "\f1b1"; }
.fi-shuffle:before { content: "\f1b2"; }
.fi-skull:before { content: "\f1b3"; }
.fi-social-500px:before { content: "\f1b4"; }
.fi-social-adobe:before { content: "\f1b5"; }
.fi-social-amazon:before { content: "\f1b6"; }
.fi-social-android:before { content: "\f1b7"; }
.fi-social-apple:before { content: "\f1b8"; }
.fi-social-behance:before { content: "\f1b9"; }
.fi-social-bing:before { content: "\f1ba"; }
.fi-social-blogger:before { content: "\f1bb"; }
.fi-social-delicious:before { content: "\f1bc"; }
.fi-social-designer-news:before { content: "\f1bd"; }
.fi-social-deviant-art:before { content: "\f1be"; }
.fi-social-digg:before { content: "\f1bf"; }
.fi-social-dribbble:before { content: "\f1c0"; }
.fi-social-drive:before { content: "\f1c1"; }
.fi-social-dropbox:before { content: "\f1c2"; }
.fi-social-evernote:before { content: "\f1c3"; }
.fi-social-facebook:before { content: "\f1c4"; }
.fi-social-flickr:before { content: "\f1c5"; }
.fi-social-forrst:before { content: "\f1c6"; }
.fi-social-foursquare:before { content: "\f1c7"; }
.fi-social-game-center:before { content: "\f1c8"; }
.fi-social-github:before { content: "\f1c9"; }
.fi-social-google-plus:before { content: "\f1ca"; }
.fi-social-hacker-news:before { content: "\f1cb"; }
.fi-social-hi5:before { content: "\f1cc"; }
.fi-social-instagram:before { content: "\f1cd"; }
.fi-social-joomla:before { content: "\f1ce"; }
.fi-social-lastfm:before { content: "\f1cf"; }
.fi-social-linkedin:before { content: "\f1d0"; }
.fi-social-medium:before { content: "\f1d1"; }
.fi-social-myspace:before { content: "\f1d2"; }
.fi-social-orkut:before { content: "\f1d3"; }
.fi-social-path:before { content: "\f1d4"; }
.fi-social-picasa:before { content: "\f1d5"; }
.fi-social-pinterest:before { content: "\f1d6"; }
.fi-social-rdio:before { content: "\f1d7"; }
.fi-social-reddit:before { content: "\f1d8"; }
.fi-social-skillshare:before { content: "\f1d9"; }
.fi-social-skype:before { content: "\f1da"; }
.fi-social-smashing-mag:before { content: "\f1db"; }
.fi-social-snapchat:before { content: "\f1dc"; }
.fi-social-spotify:before { content: "\f1dd"; }
.fi-social-squidoo:before { content: "\f1de"; }
.fi-social-stack-overflow:before { content: "\f1df"; }
.fi-social-steam:before { content: "\f1e0"; }
.fi-social-stumbleupon:before { content: "\f1e1"; }
.fi-social-treehouse:before { content: "\f1e2"; }
.fi-social-tumblr:before { content: "\f1e3"; }
.fi-social-twitter:before { content: "\f1e4"; }
.fi-social-vimeo:before { content: "\f1e5"; }
.fi-social-windows:before { content: "\f1e6"; }
.fi-social-xbox:before { content: "\f1e7"; }
.fi-social-yahoo:before { content: "\f1e8"; }
.fi-social-yelp:before { content: "\f1e9"; }
.fi-social-youtube:before { content: "\f1ea"; }
.fi-social-zerply:before { content: "\f1eb"; }
.fi-social-zurb:before { content: "\f1ec"; }
.fi-sound:before { content: "\f1ed"; }
.fi-star:before { content: "\f1ee"; }
.fi-stop:before { content: "\f1ef"; }
.fi-strikethrough:before { content: "\f1f0"; }
.fi-subscript:before { content: "\f1f1"; }
.fi-superscript:before { content: "\f1f2"; }
.fi-tablet-landscape:before { content: "\f1f3"; }
.fi-tablet-portrait:before { content: "\f1f4"; }
.fi-target-two:before { content: "\f1f5"; }
.fi-target:before { content: "\f1f6"; }
.fi-telephone-accessible:before { content: "\f1f7"; }
.fi-telephone:before { content: "\f1f8"; }
.fi-text-color:before { content: "\f1f9"; }
.fi-thumbnails:before { content: "\f1fa"; }
.fi-ticket:before { content: "\f1fb"; }
.fi-torso-business:before { content: "\f1fc"; }
.fi-torso-female:before { content: "\f1fd"; }
.fi-torso:before { content: "\f1fe"; }
.fi-torsos-all-female:before { content: "\f1ff"; }
.fi-torsos-all:before { content: "\f200"; }
.fi-torsos-female-male:before { content: "\f201"; }
.fi-torsos-male-female:before { content: "\f202"; }
.fi-torsos:before { content: "\f203"; }
.fi-trash:before { content: "\f204"; }
.fi-trees:before { content: "\f205"; }
.fi-trophy:before { content: "\f206"; }
.fi-underline:before { content: "\f207"; }
.fi-universal-access:before { content: "\f208"; }
.fi-unlink:before { content: "\f209"; }
.fi-unlock:before { content: "\f20a"; }
.fi-upload-cloud:before { content: "\f20b"; }
.fi-upload:before { content: "\f20c"; }
.fi-usb:before { content: "\f20d"; }
.fi-video:before { content: "\f20e"; }
.fi-volume-none:before { content: "\f20f"; }
.fi-volume-strike:before { content: "\f210"; }
.fi-volume:before { content: "\f211"; }
.fi-web:before { content: "\f212"; }
.fi-wheelchair:before { content: "\f213"; }
.fi-widget:before { content: "\f214"; }
.fi-wrench:before { content: "\f215"; }
.fi-x-circle:before { content: "\f216"; }
.fi-x:before { content: "\f217"; }
.fi-yen:before { content: "\f218"; }
.fi-zoom-in:before { content: "\f219"; }
.fi-zoom-out:before { content: "\f21a"; }
File diff suppressed because it is too large Load Diff
-100
View File
@@ -1,100 +0,0 @@
<!doctype html>
<html class="no-js" lang="zh-CN">
<head>
<meta charset="utf-8" />
<!-- 在IE运行最新的渲染模式 -->
<meta http-equiv="x-ua-compatible" content="ie=edge">
<!-- 初始化移动浏览显示 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>APP Inventor案例库网站</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/css/foundation.min.css"/>
</head>
<body>
<form data-abide novalidate>
<div data-abide-error class="alert callout" style="display: none;">
<p><i class="fi-alert"></i> There are some errors in your form.</p>
</div>
<div class="row">
<div class="small-12 columns">
<label>Number Required
<input type="text" placeholder="1234" aria-describedby="exampleHelpText" required pattern="number">
<span class="form-error">
Yo, you had better fill this out, it's required.
</span>
</label>
<p class="help-text" id="exampleHelpText">Here's how you use this input field!</p>
</div>
<div class="small-12 columns">
<label>Re-enter Password
<input type="password" placeholder="yeti4preZ" aria-describedby="exampleHelpText2" required pattern="alpha_numeric" data-equalto="password">
<span class="form-error">
Hey, passwords are supposed to match!
</span>
</label>
<p class="help-text" id="exampleHelpText2">This field is using the `data-equalto="password"` attribute, causing it to match the password field above.</p>
</div>
</div>
<div class="row">
<div class="large-6 columns">
<label>URL Pattern, not required, but throws error if it doesn't match the Regular Expression for a valid URL.
<input type="text" placeholder="https://foundation.zurb.com" pattern="url">
</label>
</div>
<div class="large-6 columns">
<label>Website Pattern, not required, but throws error if it doesn't match the Regular Expression for a valid URL or a Domain.
<input type="text" placeholder="https://zurb.com or zurb.com" pattern="website">
</label>
</div>
</div>
<div class="row">
<div class="large-6 columns">
<label>European Cars, Choose One, it can't be the blank option.
<select id="select" required>
<option value=""></option>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
</label>
</div>
<fieldset class="large-6 columns">
<legend>Check these out</legend>
<input id="checkbox1" type="checkbox"><label for="checkbox1">Checkbox 1</label>
<input id="checkbox2" type="checkbox" required><label for="checkbox2">Checkbox 2</label>
<input id="checkbox3" type="checkbox"><label for="checkbox3">Checkbox 3</label>
</fieldset>
</div>
<div class="row">
<fieldset class="large-6 columns">
<legend>Choose Your Favorite - not required, you can leave this one blank.</legend>
<input type="radio" name="pockets" value="Red" id="pocketsRed"><label for="pocketsRed">Red</label>
<input type="radio" name="pockets" value="Blue" id="pocketsBlue"><label for="pocketsBlue">Blue</label>
<input type="radio" name="pockets" value="Yellow" id="pocketsYellow"><label for="pocketsYellow">Yellow</label>
</fieldset>
<fieldset class="large-6 columns">
<legend>Choose Your Favorite, and this is required, so you have to pick one.</legend>
<input type="radio" name="pokemon" value="Red" id="pokemonRed"><label for="pokemonRed">Red</label>
<input type="radio" name="pokemon" value="Blue" id="pokemonBlue" required><label for="pokemonBlue">Blue</label>
<input type="radio" name="pokemon" value="Yellow" id="pokemonYellow"><label for="pokemonYellow">Yellow</label>
</fieldset>
</div>
<div class="row">
<fieldset class="large-6 columns">
<button class="button" type="submit" value="Submit">Submit</button>
</fieldset>
<fieldset class="large-6 columns">
<button class="button" type="reset" value="Reset">Reset</button>
</fieldset>
</div>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/what-input/5.0.3/what-input.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.4.3/js/foundation.min.js"></script>
<script>
$(document).foundation();
</script>
</body>
</html>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,23 @@
<template>
<div class="media">
<a class="media-left" href="#">
<img class="media-object" v-bind:data-src="thumbnailSrc" alt="Generic placeholder image">
</a>
<div class="media-body">
<h4 class="media-heading" v-text="title"></h4>
{{content}}
</div>
</div>
</template>
<script>
export default {
// <template> replace false
replace: true,
props: {
thumbnailSrc: String,
title: String,
content: String
}
};
</script>
@@ -1,191 +0,0 @@
<template>
<!-- 置顶导航 -->
<nav class="top-bar topbar-responsive">
<div class="top-bar-title">
<!-- data-responsive-toggle指定触发显示/消失的对象的id如果屏幕的大小大于640px则不显示该触发按钮 -->
<span data-responsive-toggle="topbar-responsive" data-hide-for="medium">
<!--- 附带data-toggle属性的元素负责真正事件的触发 -->
<button class="menu-icon" type="button" data-toggle></button>
</span>
<a class="topbar-responsive-logo" href="#"><strong>APP Inventor案例库</strong></a>
</div>
<div id="topbar-responsive" class="topbar-responsive-links">
<div class="top-bar-right">
<!-- Add the .simple class to a Menu to remove the padding and color change -->
<ul class="menu simple vertical medium-horizontal">
<li><a href="/">首页</a></li>
<li><a href="#">案例</a></li>
<li><a href="#">发表</a></li>
<li><a href="#">问答</a></li>
<li><input type="search" placeholder="Search"></li>
<li>
<button type="button" class="button hollow topbar-responsive-button" v-on:click='toLoginPage'>登录
</button>
</li>
<li class="menu-text" style="color:gray">Or</li>
<li>
<button type="button" class="button hollow topbar-responsive-button" v-on:click='toRegisterPage'>注册
</button>
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
import $ from 'jquery';
export default {
// <template> replace false
replace: true,
props: {
pageType: String
},
methods: {
toLoginPage: function () {
this.$root.jumpToThisPage('login');
},
toRegisterPage: function () {
this.$root.jumpToThisPage('register');
}
},
mounted: function () {
this.$nextTick(function () {
var prev = 0;
var $window = $(window);
var nav = $('.scrollhide-nav');
$window.on('scroll', function () {
var scrollTop = $window.scrollTop();
nav.toggleClass('hidden', scrollTop > prev);
prev = scrollTop;
});
});
}
};
</script>
<style lang="scss">
@import "../assets/scss/constant";
@import "../../node_modules/foundation-sites/scss/util/util";
$topbar-responsive-bg: #2c3840;
$topbar-responsive-animation-type: fade-in; // or use slide-down or none
.or-text {
color: $White;
}
.topbar-responsive {
background: $topbar-responsive-bg;
padding: 1rem 1.5rem;
// +
.topbar-responsive-logo {
color: $White;
vertical-align: middle;
}
.menu {
background: $topbar-responsive-bg;
li:last-of-type {
margin-right: 0;
}
a {
color: $White;
transition: color 0.15s ease-in;
&:hover {
color: lighten($topbar-responsive-bg, 60%);
}
@media screen and (max-width: 39.9375em) {
padding: 0.875rem 0;
}
}
.topbar-responsive-button {
color: $White;
border-color: $White;
// div
border-radius: 500px;
// transition: property duration timing-function delay;
// propertyCSS CSS
// ease-in
transition: color 0.15s ease-in, border-color 0.15s ease-in;
// `&`使
&:hover {
color: lighten($topbar-responsive-bg, 60%);
border-color: lighten($topbar-responsive-bg, 60%);
}
@media screen and (max-width: 39.9375em) {
width: 100%;
margin: 0.875rem 0;
}
}
}
@media screen and (max-width: 39.9375em) {
padding: 0.75rem;
.top-bar-title {
position: relative;
width: 100%;
span {
position: absolute;
right: 0;
border: 1px solid $White;
border-radius: 5px;
padding: 0.25rem 0.45rem;
top: 50%;
transform: translateY(-50%);
.menu-icon {
margin-bottom: 4px;
}
}
}
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slideDown {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(0%);
}
}
@include breakpoint(small only) {
.topbar-responsive-links {
// forwards
// backwards animation-delay
animation-fill-mode: both;
animation-duration: 0.5s;
width: 100%;
// animation: name duration timing-function delay iteration-count direction;
// ease
@if $topbar-responsive-animation-type == slide-down {
animation: slideDown 1s ease-in;
} @else if $topbar-responsive-animation-type == fade-in {
animation: fadeIn 1s ease-in;
} @else {
animation: none;
}
}
}
</style>
@@ -0,0 +1,42 @@
<template>
<div id="indexCarousel" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ol class="carousel-indicators">
<template v-for="(bannerCase, index) in bannerCaseList">
<li data-target="#indexCarousel" v-bind:data-slide-to="index" v-bind:class="[index===0?'active':'']"></li>
</template>
</ol>
<!-- Wrapper for slides -->
<div class="carousel-inner" role="listbox">
<template v-for="(bannerCase, index) in bannerCaseList">
<div v-bind:class="[index===0?'active':'', 'item']">
<img v-bind:src="bannerCase.imageSrc">
<div class="carousel-caption">
{{bannerCase.title}}
</div>
</div>
</template>
</div>
<!-- Controls -->
<a class="left carousel-control" href="#indexCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#indexCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
</template>
<script>
export default {
data() {
return {
bannerCaseList: []
};
}
};
</script>
@@ -0,0 +1,41 @@
<template>
<div class="panel-group" id="leftAccordionMenu" role="tablist" aria-multiselectable="true">
<div v-for="(parentCategory, index) in categoryList" class="panel panel-default">
<div class="panel-heading" role="tab">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#leftAccordionMenu" v-bind:href="'#collapse'+index" v-bind:class="[index===0?'':'collapsed']" v-bind:aria-expanded="index===0?'true':'false'" v-bind:aria-controls="'collapse'+index">
{{parentCategory.name}}
</a>
</h4>
</div>
<div v-bind:id="'collapse'+index" v-bind:class="[index===0?'in':'', 'panel-collapse','collapse']" role="tabpanel">
<ul v-for="subCategory in parentCategory.sub_cat" class="list-group">
<li class="list-group-item">{{subCategory.name}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
categoryList: []
};
},
mounted: function () {
this.$nextTick(function () {
//
this.$http.get('categorys/')
.then((res) => {
this.categoryList = res.data;
}, (err) => {
var errorReasonDict = err.body;
console.log('---errorReasonDict---');
console.log(errorReasonDict);
});
});
}
};
</script>
@@ -0,0 +1,84 @@
<template>
<form data-toggle="validator" role="form" id="loginForm">
<div class="form-group">
<i class="fa fa-user-circle fa-lg" aria-hidden="true"></i>
<input type="text" class="form-control" name="username" placeholder="请输入用户名" data-pattern-error="用户名长度不足6位" data-required-error="用户名不可以为空" pattern="^.{6,}$" required data-serverValidation>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<i class="fa fa-lock fa-lg" aria-hidden="true"></i>
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" data-pattern-error="密码长度不足6位" data-required-error="密码不可以为空" pattern="^.{6,}$" required data-serverValidation>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<button class="button expanded" v-on:click='postLoginData'>登录</button>
</div>
</form>
</template>
<script>
import $ from 'jquery';
import Vue from 'vue';
export default {
data() {
return {
serverValidationResult: {}
};
},
mounted: function () {
this.$nextTick(function () {
//
$('#loginForm').submit(function (e) {
e.preventDefault();
});
$('#loginForm i').css('margin-left', '2%');
$('#loginForm input').css('display', 'inline-block').css('width', '70%').css('margin-left', '2%');
$('#loginForm').validator({
custom: {
serverValidation: function ($el) {
//
var inputFieldName = $el.attr('name');
if (inputFieldName in this.serverValidationResult) {
var result = this.serverValidationResult[inputFieldName];
return result;
} else {
return false;
}
}.bind(this)
}
});
});
},
methods: {
postLoginData: function () {
var errorNum = $('#loginForm').validator('validate').has('.has-error').length;
if (errorNum > 0) {
return;
}
var postData = this.$root.getFormInput('loginForm');
this.$http.post('login/', postData)
.then((res) => {
Vue.http.headers.common['Authorization'] = 'JWT ' + res.data['token'];
this.setUserName($('#loginForm input[name="username"]').val());
this.$emit('closeLoginModal');
}, (err) => {
var errorReasonDict = err.body;
console.log('---errorReasonDict---');
console.log(errorReasonDict);
for (var key in errorReasonDict) {
this.serverValidationResult[key] = errorReasonDict[key];
};
$('#loginForm').validator('validate');
this.serverValidationResult = {};
});
},
setUserName: function(userName) {
this.$store.commit('setUserName', userName);
window.sessionStorage.userName = userName;
}
}
};
</script>
@@ -0,0 +1,136 @@
<template>
<form data-toggle="validator" role="form" id="registerForm">
<div class="form-group">
<i class="fa fa-user-circle fa-lg" aria-hidden="true"></i>
<input type="text" class="form-control" name="username" placeholder="请输入用户名" data-pattern-error="用户名长度不足6位" data-required-error="用户名不可以为空" pattern="^.{6,}$" required data-serverValidation>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<i class="fa fa-envelope fa-lg" aria-hidden="true"></i>
<input type="email" class="form-control" name="email" placeholder="请输入邮箱地址" data-error="邮箱地址格式非法" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<i class="fa fa-envelope fa-lg" aria-hidden="true"></i>
<input type="text" class="form-control" name="mobile" placeholder="(非必填)请输入手机号码" data-pattern-error="手机号码格式错误" pattern="^1[34578]\d{9}$">
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<i class="fa fa-lock fa-lg" aria-hidden="true"></i>
<input type="password" class="form-control" id="password" name="password" placeholder="请输入密码" data-pattern-error="密码长度不足6位" data-required-error="密码不可以为空" pattern="^.{6,}$" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<i class="fa fa-lock fa-lg" aria-hidden="true"></i>
<input type="password" class="form-control" name="repeated_password" placeholder="请重复上面所输入的密码" data-match-error="两次输入的密码不一样" data-required-error="必须重复上面的密码" data-match="#password" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<img v-bind:src="pictureCodeSrc" style="cursor: pointer" v-on:click='requestForPictureCode'>
<input type="text" class="form-control" name="code" placeholder="请输入图片验证码" data-pattern-error="验证码的长度必须为4位" data-required-error="验证码不可以为空" pattern="^.{4,4}$" required data-serverValidation>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<input type="hidden" name="picture_token" v-bind:value="pictureToken"/>
</div>
<div class="form-group">
<button class="button expanded" v-on:click='postRegisterData'>注册</button>
</div>
</form>
</template>
<script>
import $ from 'jquery';
import Vue from 'vue';
export default {
data() {
return {
pictureCodeSrc: '',
pictureToken: '',
serverValidationResult: {}
};
},
methods: {
postRegisterData: function () {
var errorNum = $('#registerForm').validator('validate').has('.has-error').length;
if (errorNum > 0) {
return;
}
var postData = this.$root.getFormInput('registerForm');
this.$http.post('users/', postData)
.then((res) => {
Vue.http.headers.common['Authorization'] = 'JWT ' + res.data['jwt_token'];
this.setUserName($('#registerForm input[name="username"]').val());
this.$emit('closeRegisterModal');
}, (err) => {
var errorReasonDict = err.body;
console.log('---errorReasonDict---');
console.log(errorReasonDict);
for (var key in errorReasonDict) {
if (key === 'non_field_errors') {
var reason = errorReasonDict[key][0];
if (reason.indexOf('令牌') === -1) {
this.serverValidationResult['code'] = errorReasonDict[key];
}
this.requestForPictureCode();
} else if (key === 'picture_token') {
this.requestForPictureCode();
} else {
this.serverValidationResult[key] = errorReasonDict[key];
}
};
$('#registerForm').validator('validate');
this.serverValidationResult = {};
});
},
//
requestForPictureCode: function () {
this.$http.get('picturecodes/')
.then((res) => {
this.pictureCodeSrc = this.$root.$data.requestHost + res.data['cptch_image'];
this.pictureToken = res.data['cptch_key'];
$('#registerForm input[name="code"]').val('');
}, (err) => {
console.log('图片验证码请求错误');
console.log(err);
});
},
setUserName: function(userName) {
this.$store.commit('setUserName', userName);
window.sessionStorage.userName = userName;
}
},
mounted: function () {
this.$nextTick(function () {
//
$('#registerForm').submit(function (e) {
e.preventDefault();
});
$('#registerForm i').css('margin-left', '2%');
$('#registerForm input[name]').css('display', 'inline-block').css('width', '70%').css('margin-left', '2%');
$('#registerForm').validator({
custom: {
serverValidation: function ($el) {
//
var inputFieldName = $el.attr('name');
if (inputFieldName in this.serverValidationResult) {
var result = this.serverValidationResult[inputFieldName];
return result;
} else {
return false;
}
}.bind(this)
}
});
});
}
};
</script>
@@ -0,0 +1,199 @@
<template>
<form id="richTextEditorForm">
<div class="form-group">
<label>案例名称</label>
<input type="text" class="form-control" name="name" placeholder="请输入案例名称" data-required-error="案例名称不可以为空" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label>案例简短描述</label>
<input type="text" class="form-control" name="cases_brief" placeholder="请输入案例简短描述" data-required-error="案例简短描述不可以为空" required>
<div class="help-block with-errors"></div>
</div>
<div class="form-group">
<label>案例类别</label>
<select name="category_name">
<optgroup v-for="parentCategory in categoryList" v-bind:label="parentCategory.name">
<option v-for="subCategory in parentCategory.sub_cat">{{subCategory.name}}</option>
</optgroup>
</select>
<div class="help-block with-errors"></div>
</div>
<textarea name="cases_desc">
<p style="text-align: center;">
<img title="TinyMCE Logo" src="//www.tinymce.com/images/glyph-tinymce@2x.png" alt="TinyMCE Logo" width="110"
height="97"/></p>
<h1 style="text-align: center;">Welcome to the TinyMCE editor demo!</h1>
<p>Please try out the features provided in this basic example.
<br>Note that any
<strong>MoxieManager</strong>file and image management functionality in this example is part of our commercial offering the demo is to show the integration.
</p>
<h2>Got questions or need help?</h2>
<ul>
<li>Our<a href="https://www.tinymce.com/docs/">documentation</a>is a great resource for learning how to configure TinyMCE.</li>
<li>Have a specific question? Visit the<a href="https://community.tinymce.com/forum/" target="_blank">Community Forum</a>.</li>
<li>We also offer enterprise grade support as part of<a
href="www.tinymce.com/pricing">TinyMCE Enterprise</a>.</li></ul>
<h2>A simple table to play with</h2>
<table style="text-align: center;">
<thead>
<tr>
<th>Product</th>
<th>Cost</th>
<th>Really?</th></tr>
</thead>
<tbody>
<tr>
<td>TinyMCE</td>
<td>Free</td>
<td>YES!</td></tr>
<tr>
<td>Plupload</td>
<td>Free</td>
<td>YES!</td></tr>
</tbody>
</table>
<h2>Found a bug?</h2>
<p>If you think you have found a bug please create an issue on the
<a href="https://github.com/tinymce/tinymce/issues">GitHub repo</a>to report it to the developers.
</p>
<h2>Finally ...</h2>
<p>Don't forget to check out our other product
<a href="http://www.plupload.com" target="_blank">Plupload</a>, your ultimate upload solution featuring HTML5 upload support.</p>
<p>Thanks for supporting TinyMCE! We hope it helps you and your users create great content.
<br>All the best from the TinyMCE team.</p>
</textarea>
<div class="form-group">
<label>案例封面图片</label>
<input type="file" v-on:change="uploadCoverPicture" id="coverPictureSelector">
<label>上传进度{{uploadProgress}}%</label>
</div>
<div class="form-group">
<input type="hidden" name="cases_front_image" v-bind:value="coverPictureURL"/>
</div>
<button class="button expanded" v-on:click='postRichTextEditorData'>提交</button>
</form>
</template>
<script>
import $ from 'jquery';
export default {
data() {
return {
categoryList: [],
coverPictureURL: '',
uploadProgress: 0.0
};
},
methods: {
postRichTextEditorData: function () {
window.tinymce.activeEditor.uploadImages(function(success) {
var postData = this.$root.getFormInput('richTextEditorForm');
this.$http.post('cases/', postData).then(function(res) {
console.log('---表单提交成功---');
}, (err) => {
console.log('---err.body---');
console.log(err.body);
});
}.bind(this));
},
uploadCoverPicture (event) {
this.uploadProgress = 0.0;
this.coverPictureURL = '';
let file = event.target.files[0];
if (file) {
const formData = new FormData();
formData.append('file', file);
this.$http.post('uploadfile/case_cover_picture/', formData, {
progress: function(event) {
this.uploadProgress = parseFloat(event.loaded / event.total * 100);
}.bind(this)
}).then((res) => {
this.coverPictureURL = res.data['location'];
console.log('---this.coverPictureURL---');
console.log(this.coverPictureURL);
}, (err) => {
this.uploadProgress = 0.0;
var errorReasonDict = err.body;
console.log('---errorReasonDict---');
console.log(errorReasonDict);
});
}
}
},
mounted: function () {
this.$nextTick(function () {
//
$('#richTextEditorForm').submit(function (e) {
e.preventDefault();
});
//
this.$http.get('categorys/')
.then((res) => {
this.categoryList = res.data;
}, (err) => {
var errorReasonDict = err.body;
console.log('---errorReasonDict---');
console.log(errorReasonDict);
});
// TinyMCE
if (!window.tinymce) {
$.getScript('https://cloud.tinymce.com/stable/tinymce.min.js?apiKey=w1jnifw6jyuhsi04yjizbcmplt1s0w5zks3u7xeaitd7samr')
.done(function() {
// DOMwindow.tinymcetinymce
this.$nextTick(function () {
// TinyMCE
window.tinymce.init({
selector: 'textarea',
branding: false,
language_url: this.$root.$data.requestHost + '/static/zh_CN.js', // site absolute URL
height: 500,
menubar: false,
plugins: [
'advlist autolink lists link image charmap print preview anchor textcolor colorpicker',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table contextmenu paste code help'
],
toolbar: 'insert | undo redo | formatselect image | bold italic strikethrough forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help',
//
images_upload_url: this.$root.$data.requestHost + '/uploadfile/rich_text_picture/',
images_upload_handler: function (blobInfo, success, failure) {
var formData = new FormData();
formData.append('file', blobInfo.blob(), blobInfo.filename());
this.$http.post('uploadfile/rich_text_picture/', formData).then((res) => {
success(res.data['location']);
}, (err) => {
var errorReasonDict = err.body;
console.log('---errorReasonDict---');
console.log(errorReasonDict);
failure(errorReasonDict['detail']);
});
}.bind(this),
content_css: [
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
'//www.tinymce.com/css/codepen.min.css']
});
});
}.bind(this))
.fail(function() {
console.log('TinyMCE加载失败。。。');
});
}
});
}
};
</script>
<style>
body {
padding: 5px
}
</style>
@@ -0,0 +1,136 @@
<template>
<div>
<nav class="navbar navbar-default">
<!-- 占满整行 -->
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">APP Inventor案例库</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav" id="navUl">
<li><a href="/index">首页<span class="sr-only">(current)</span></a></li>
<li><a href="#">案例</a></li>
<li><a href="/editCase">发表</a></li>
<li><a href="#">问答</a></li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="请输入搜索关键字">
</div>
<button type="submit" class="btn btn-default">搜索</button>
</form>
<template v-if="userName.length > 0">
<ul class="nav navbar-nav navbar-right">
<li><button type="button" class="btn btn-default navbar-btn" v-on:click='logout'>注销</button></li>
</ul>
</template>
<template v-else>
<ul class="nav navbar-nav navbar-right">
<li><button type="button" class="btn btn-default navbar-btn" v-on:click="openLoginModal">登录</button></li>
<li><p class="navbar-text">Or</p></li>
<li><button type="button" class="btn btn-default navbar-btn" v-on:click="openRegisterModal">注册</button></li>
</ul>
</template>
</div>
</div>
</nav>
<div class="modal fade" id="loginModal" tabindex="-1" role="dialog" aria-labelledby="loginModal">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title text-center">登录</h4>
</div>
<login-modal ref="loginModal" v-on:closeLoginModal="closeLoginModal"></login-modal>
</div>
</div>
</div>
<div class="modal fade" id="registerModal" tabindex="-1" role="dialog" aria-labelledby="registerModal">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title text-center">注册</h4>
</div>
<register-modal ref="registerModal" v-on:closeRegisterModal="closeRegisterModal"></register-modal>
</div>
</div>
</div>
</div>
</template>
<script>
import $ from 'jquery';
import Vue from 'vue';
import loginModal from './loginModal.vue';
import registerModal from './registerModal.vue';
export default {
// <template> replace false
replace: true,
props: {
activeIndex: {
type: String,
required: true
}
},
components: {
loginModal,
registerModal
},
computed: {
userName () {
return this.$store.state.userName;
}
},
methods: {
closeLoginModal: function () {
$('#loginModal').modal('hide');
},
closeRegisterModal: function () {
$('#registerModal').modal('hide');
},
openLoginModal: function () {
// backdrop static
// keyboard false Esc Modal
$('#loginModal').modal({backdrop: 'static', keyboard: false});
},
openRegisterModal: function () {
// backdrop static
// keyboard false Esc Modal
$('#registerModal').modal({backdrop: 'static', keyboard: false});
},
logout: function () {
this.$store.commit('setUserName', '');
window.sessionStorage.userName = '';
delete Vue.http.headers.common['Authorization'];
this.$router.go(0);
}
},
mounted: function () {
this.$nextTick(function () {
var $selector = $('#navUl li');
$($selector[parseInt(this.activeIndex)]).addClass('active');
$('#registerModal').on('show.bs.modal', function () {
//
this.$refs.registerModal.requestForPictureCode();
}.bind(this));
if (this.$route.query.hasOwnProperty('needToLogin')) {
this.openLoginModal();
}
console.log('---this.$store.state---');
console.log(this.$store.state);
});
}
};
</script>
+7 -14
View File
@@ -6,14 +6,11 @@ import routes from './routers';
import Alert from './libs/alert';
import store from './vuex/user';
import FastClick from 'fastclick';
import $ from 'jquery';
import Utils from './utils';
Vue.use(VueRouter);
Vue.use(Alert);
Vue.use(VueResource);
$.ajaxSettings.crossDomain = true;
/*
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组
Vue.filter('my-filter', function (value前) { return value后 })用来给Vue注册自定义过滤器
@@ -38,8 +35,8 @@ const router = new VueRouter({
FastClick.attach(document.body);
// 处理刷新的时候vuex被清空但是用户已经登录的情况
if (window.sessionStorage.user) {
store.dispatch('setUserInfo', JSON.parse(window.sessionStorage.user));
if (window.sessionStorage.userName) {
store.commit('setUserName', window.sessionStorage.userName);
}
/*
@@ -55,13 +52,15 @@ if (window.sessionStorage.user) {
next()进行管道中的下一个钩子next(false)返回到from参数对应的URLnext(其他URL)表示重定向
*/
router.beforeEach((to, from, next) => {
console.log('---store.state---');
console.log(store.state);
if (to.matched.some(record => record.meta.requiresAuth)) {
if (store.state.userInfo.userId) {
if (store.state.userName.length > 0) {
next();
} else {
next({
path: '/login',
query: { redirect: to.fullPath }
path: '/index',
query: { redirect: to.fullPath, needToLogin: 'true' }
});
}
} else {
@@ -85,11 +84,5 @@ new Vue({
return {
requestHost: requestHost
};
},
mounted: function () {
this.$nextTick(function () {
// 使得Foundation框架生效所必须的操作
$(document).foundation();
});
}
}).$mount('#app');
+10 -9
View File
@@ -18,21 +18,22 @@ const routers = [{
});
}
}, {
path: '/register',
name: 'register',
path: '/index',
name: 'index',
component(resolve) {
require.ensure(['./views/register.vue'], () => {
resolve(require('./views/register.vue'));
require.ensure(['./views/index.vue'], () => {
resolve(require('./views/index.vue'));
});
}
}, {
path: '/login',
name: 'login',
path: '/editCase',
name: 'editCase',
component(resolve) {
require.ensure(['./views/login.vue'], () => {
resolve(require('./views/login.vue'));
require.ensure(['./views/editCase.vue'], () => {
resolve(require('./views/editCase.vue'));
});
}
},
meta: { requiresAuth: true }
}, {
path: '*',
component(resolve) {
+15 -1
View File
@@ -1,5 +1,19 @@
import $ from 'jquery';
const jumpToThisPage = function (name, params) {
this.$router.push({name: name, params: params});
};
export default {jumpToThisPage};
const getFormInput = function (formId) {
var postData = {};
var fieldArray = $('#' + formId).serializeArray();
$.each(fieldArray, function (i, field) {
postData[field.name] = field.value;
});
console.log('---请求来自' + formId + '---');
console.log('---数据序列化为---');
console.log(postData);
return postData;
};
export default {jumpToThisPage, getFormInput};
@@ -0,0 +1,34 @@
<template>
<div>
<div class="row">
<div class="col-sm-12">
<top-bar activeIndex='2'>
</top-bar>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<rich-text-editor>
</rich-text-editor>
</div>
</div>
</div>
</template>
<script>
import topBar from '../components/topBar.vue';
import richTextEditor from '../components/richTextEditor.vue';
export default {
components: {
topBar,
richTextEditor
},
data() {
return {
caseList: []
};
}
};
</script>
<style lang="scss">
</style>
+40 -10
View File
@@ -1,19 +1,49 @@
<template>
<ai-head page-type="登录">
</ai-head>
<div>
<div class="row">
<div class="col-sm-12">
<top-bar activeIndex='0'>
</top-bar>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<index-carousel>
</index-carousel>
</div>
</div>
<div class="row">
<div class="col-sm-3">
<left-accordion-menu>
</left-accordion-menu>
</div>
<div class="col-sm-9">
<div class="row">
<div v-for="caseObj in caseList" class="col-sm-4">
<case-media-object thumbnailSrc=caseObj.thumbnailSrc title=caseObj.tile content=caseObj.content>
</case-media-object>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import aiHead from '../components/header.vue';
import $ from 'jquery';
import topBar from '../components/topBar.vue';
import leftAccordionMenu from '../components/leftAccordionMenu.vue';
import caseMediaObject from '../components/caseMediaObject.vue';
import indexCarousel from '../components/indexCarousel.vue';
export default {
components: {
aiHead
topBar,
leftAccordionMenu,
caseMediaObject,
indexCarousel
},
mounted: function () {
this.$nextTick(function () {
// 使Foundation
$(document).foundation();
});
data() {
return {
caseList: []
};
}
};
</script>
-88
View File
@@ -1,88 +0,0 @@
<template>
<!-- 取消H5的默认表单验证功能以免与Foundation的Abide发生冲突 -->
<form data-abide novalidate>
<div class="row form-icons">
<h4>登录</h4>
<!-- 总的错误消息提示框alert标识的是颜色 -->
<div data-abide-error class="column small-12 alert callout" style="display: none">
<p><i class="fi-alert"></i> 表单输入有错误</p>
</div>
<div class="column small-12 input-group">
<span class="input-group-label">
<i class="fi fi-torso"></i>
</span>
<!-- required表示为必填项 -->
<input class="input-group-field" type="text" name="username" placeholder="请输入用户名" required
pattern="not-shorter-than-six">
<br/>
<!-- 对应的错误提示信息 -->
<span class="form-error">用户名长度不足6位</span>
</div>
<div class="column small-12 input-group">
<span class="input-group-label">
<i class="fi fi-key"></i>
</span>
<input class="input-group-field" type="password" name="password" id="Password" placeholder="请输入密码"
required pattern="not-shorter-than-six">
<label>
<span class="form-error">密码长度不足6位</span>
</label>
</div>
</div>
<button class="button expanded">登录</button>
</form>
</template>
<script>
import $ from 'jquery';
export default {
mounted: function () {
this.$nextTick(function () {
// Pattern
window.Foundation.Abide.defaults.patterns['not-shorter-than-six'] = /^.{6,}$/;
// 使Foundation
$(document).foundation();
});
}
};
</script>
<style lang="scss">
@import "../assets/scss/constant";
form {
.form-icons {
text-align: center;
h4 {
margin-bottom: 1rem;
}
.input-group {
vertical-align: middle;
}
.form-error {
font-size: 1rem;
margin-top: 1rem;
}
.input-group-label {
background-color: $primary-color;
border-color: $primary-color;
}
.input-group-field {
border-color: $primary-color;
}
.fi {
color: white;
width: 1rem; // makes the width of icon labels equal
}
}
}
</style>
@@ -1,166 +0,0 @@
<template>
<!-- 取消H5的默认表单验证功能以免与Foundation的Abide发生冲突 -->
<form data-abide novalidate id="RegisterForm">
<div class="row form-icons">
<h4>注册</h4>
<!-- 总的错误消息提示框alert标识的是颜色 -->
<div data-abide-error class="column small-12 alert callout" style="display: none">
<p><i class="fi-alert"></i> 表单输入有错误</p>
</div>
<div class="column small-12 input-group">
<span class="input-group-label">
<i class="fi fi-torso"></i>
</span>
<!-- required表示为必填项 -->
<input class="input-group-field" type="text" name="username" placeholder="请输入用户名" required
pattern="not-shorter-than-six">
<br/>
<!-- 对应的错误提示信息 -->
<span class="form-error">用户名长度不足6位</span>
</div>
<div class="column small-12 input-group">
<span class="input-group-label">
<i class="fi fi-mail"></i>
</span>
<input class="input-group-field" type="text" name="email" placeholder="请输入邮箱地址" required
pattern="email">
<label>
<span class="form-error">邮箱地址格式非法</span>
</label>
</div>
<div class="column small-12 input-group">
<span class="input-group-label">
<i class="fi fi-telephone"></i>
</span>
<input class="input-group-field" type="text" name="mobile" placeholder="(非必填)请输入手机号码"
pattern="telephone">
<label>
<span class="form-error">手机号码格式非法</span>
</label>
</div>
<div class="column small-12 input-group">
<span class="input-group-label">
<i class="fi fi-key"></i>
</span>
<input class="input-group-field" type="password" name="password" id="Password" placeholder="请输入密码"
required pattern="not-shorter-than-six">
<label>
<span class="form-error">密码长度不足6位</span>
</label>
</div>
<div class="column small-12 input-group">
<span class="input-group-label">
<i class="fi fi-key"></i>
</span>
<input class="input-group-field" type="password" name="repeated_password" placeholder="请重复上面所输入的密码"
required data-equalto="Password">
<div class="form-error">两次输入的密码不一样</div>
</div>
<div class="column small-12 input-group">
<img v-bind:src="pictureCodeSrc">
<input class="input-group-field" type="text" name="code" placeholder="请输入图片验证码"
required pattern="length-equalto-four">
<div class="form-error">图片验证码长度为4</div>
</div>
<input type="hidden" name="picture_token" v-bind:value="pictureToken"/>
</div>
<button class="button expanded" v-on:click='postRegisterData'>注册</button>
</form>
</template>
<script>
import $ from 'jquery';
import Vue from 'vue';
export default {
data() {
return {
pictureCodeSrc: '',
pictureToken: ''
};
},
methods: {
postRegisterData: function () {
var postData = {};
var fieldArray = $('#RegisterForm').serializeArray();
$.each(fieldArray, function (i, field) {
postData[field.name] = field.value;
});
this.$http.post(this.$root.$data.requestHost + '/users/', postData)
.then((res) => {
Vue.http.headers.common['Authorization'] = 'JWT ' + res.data['jwt_token'];
console.log('JWT ' + res.data['jwt_token']);
this.$root.jumpToThisPage('/');
}, (err) => {
console.log('网络请求错误');
console.log(err);
});
}
},
mounted: function () {
this.$nextTick(function () {
// Pattern
window.Foundation.Abide.defaults.patterns['not-shorter-than-six'] = /^.{6,}$/;
window.Foundation.Abide.defaults.patterns['length-equalto-four'] = /^.{4,4}$/;
window.Foundation.Abide.defaults.patterns['telephone'] = /^1[34578]\d{9}$/;
// 使Foundation
$(document).foundation();
//
this.$http.get('picturecodes/')
.then((res) => {
this.pictureCodeSrc = this.$root.$data.requestHost + res.data['cptch_image'];
this.pictureToken = res.data['cptch_key'];
}, (err) => {
console.log('图片验证码请求错误');
console.log(err);
});
//
$('#RegisterForm').submit(function (e) {
e.preventDefault();
});
});
}
};
</script>
<style lang="scss">
@import "../assets/scss/constant";
form {
.form-icons {
text-align: center;
h4 {
margin-bottom: 1rem;
}
.input-group {
vertical-align: middle;
}
.form-error {
font-size: 1rem;
margin-top: 1rem;
}
.input-group-label {
background-color: $primary-color;
border-color: $primary-color;
}
.input-group-field {
border-color: $primary-color;
}
.fi {
color: white;
width: 1rem; // makes the width of icon labels equal
}
}
}
</style>
+3 -20
View File
@@ -4,29 +4,12 @@ Vue.use(Vuex);
const userStore = new Vuex.Store({
state: {
userInfo: {}
},
getters: {
getUserInfo(state) {
return state.userInfo;
}
userName: ''
},
// 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
mutations: {
setUserInfo(state, userInfo) {
state.userInfo = userInfo;
}
},
/*
Action 类似于 mutation不同在于①Action提交的是mutation而不是直接变更状态②Action可以包含任意异步操作
Action 函数接受一个与store 实例具有相同方法和属性的context对象因此你可以调用context.commit提交一个mutation
或者通过context.state和context.getters来获取state和getters但是context 对象并不是store实例本身
*/
actions: {
// {commit}这种写法属于ES6新特性,属于对象的解构赋值,得到的commit相当于context.commit
setUserInfo({ commit }, user) {
// user为mutation的载荷(payload),这里会引发setUserInfo(state, user)这个mutation
commit('setUserInfo', user);
setUserName(state, userName) {
state.userName = userName;
}
}
});
+9
View File
@@ -0,0 +1,9 @@
# HTML笔记
## 1.display属性
①block:块级元素,前后会带有换行符
(代表元素有:DIV, FORM, TABLE, P, PRE, H1~H6, DL, OL, UL
②inline:内联元素,不自动进行换行符
(代表元素有:SPAN, A, STRONG, EM, LABEL, INPUT, SELECT, TEXTAREA, IMG, BR
③inline-block:将对象呈现为inline对象,但是对象的内容作为block对象呈现。使其既具有block的宽度高度特性又具有inline的同行特性
+4 -4
View File
@@ -1,6 +1,6 @@
# 里程碑
1. 完成后端Django REST framework项目框架(附注释)
2. 完成前端webpack+Vue全家桶的前端项目框架(附注释)
3. 利用Foundation完成了顶部导航栏、注册、登录的页面设计
4. 完成注册页面和逻辑功能
1. 完成后端Xadmin+Django全家桶(①django;②django-REST-framework;③django-filter;④django-simple-captcha;⑤django-REST-framework-jwt 项目框架(附注释)
2. 完成前端webpack+Vue全家桶(①Vue2;②Vue-router;③Vue-resource;④Vuex的前端项目框架(附注释)
3. 利用BootStrap3完成了顶部导航栏、注册、登录、案例发布的页面设计
4. 利用Django REST framework完成注册、登录、案例发布的后台业务逻辑代码的编写
+16
View File
@@ -0,0 +1,16 @@
# Vue笔记
1. v-model是v-bind和v-on的语法糖。差不多就相当于下面代码:
```
<input id="a" class="check" name="style" type="checkbox" v-bind:checked="need" v-on:change="change">
data:{
need:true
},
methods:{
change:function(e){
this.need = e.target.value;
}
}
```
+5 -14
View File
@@ -3,15 +3,13 @@ __author__ = 'HymanLu'
import xadmin
from .models import Cases, CasesCategory, CasesImage, Banner, HotSearchWords
from .models import IndexAd
class CasesAdmin(object):
list_display = ["name", "click_num", "sold_num", "fav_num", "cases_num", "market_price",
"shop_price", "cases_brief", "cases_desc", "is_new", "is_hot", "add_time"]
list_display = ["name", "click_num", "fav_num",
"cases_brief", "cases_desc", "add_time"]
search_fields = ['name', ]
list_editable = ["is_hot", ]
list_filter = ["name", "click_num", "sold_num", "fav_num", "cases_num", "market_price",
"shop_price", "is_new", "is_hot", "add_time", "category__name"]
list_filter = ["name", "click_num", "fav_num",
"add_time", "category__name"]
style_fields = {"cases_desc": "ueditor"}
class CasesImagesInline(object):
@@ -47,14 +45,7 @@ class HotSearchAdmin(object):
list_display = ["keywords", "index", "add_time"]
class IndexAdAdmin(object):
list_display = ["category", "cases"]
xadmin.site.register(Cases, CasesAdmin)
xadmin.site.register(CasesCategory, CasesCategoryAdmin)
xadmin.site.register(Banner, BannerCasesAdmin)
xadmin.site.register(HotSearchWords, HotSearchAdmin)
xadmin.site.register(IndexAd, IndexAdAdmin)
xadmin.site.register(HotSearchWords, HotSearchAdmin)
+1 -3
View File
@@ -11,8 +11,6 @@ class CasesFilter(django_filters.rest_framework.FilterSet):
"""
案例的过滤类
"""
pricemin = django_filters.NumberFilter(name='shop_price', help_text="最低价格",lookup_expr='gte')
pricemax = django_filters.NumberFilter(name='shop_price', lookup_expr='lte')
top_category = django_filters.NumberFilter(method='top_category_filter')
@@ -22,4 +20,4 @@ class CasesFilter(django_filters.rest_framework.FilterSet):
class Meta:
model = Cases
fields = ['pricemin', 'pricemax', 'is_hot', 'is_new']
fields = []
@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.7 on 2017-11-14 17:39
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('cases', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='indexad',
name='cases',
),
migrations.RemoveField(
model_name='indexad',
name='category',
),
migrations.RemoveField(
model_name='cases',
name='cases_num',
),
migrations.RemoveField(
model_name='cases',
name='cases_sn',
),
migrations.RemoveField(
model_name='cases',
name='is_hot',
),
migrations.RemoveField(
model_name='cases',
name='is_new',
),
migrations.RemoveField(
model_name='cases',
name='market_price',
),
migrations.RemoveField(
model_name='cases',
name='ship_free',
),
migrations.RemoveField(
model_name='cases',
name='shop_price',
),
migrations.RemoveField(
model_name='cases',
name='sold_num',
),
migrations.DeleteModel(
name='IndexAd',
),
]
+4 -24
View File
@@ -34,21 +34,14 @@ class Cases(models.Model):
案例
"""
category = models.ForeignKey(CasesCategory, verbose_name="案例类目")
cases_sn = models.CharField(max_length=50, default="", verbose_name="案例唯一货号")
name = models.CharField(max_length=100, verbose_name="案例名")
click_num = models.IntegerField(default=0, verbose_name="点击数")
sold_num = models.IntegerField(default=0, verbose_name="案例销售量")
fav_num = models.IntegerField(default=0, verbose_name="收藏数")
cases_num = models.IntegerField(default=0, verbose_name="库存数")
market_price = models.FloatField(default=0, verbose_name="市场价格")
shop_price = models.FloatField(default=0, verbose_name="本店价格")
cases_brief = models.TextField(max_length=500, verbose_name="案例简短描述")
cases_desc = UEditorField(verbose_name=u"内容", imagePath="cases/images/", width=1000, height=300,
filePath="cases/files/", default='')
ship_free = models.BooleanField(default=True, verbose_name="是否承担运费")
cases_front_image = models.ImageField(upload_to="cases/images/", null=True, blank=True, verbose_name="封面图")
is_new = models.BooleanField(default=False, verbose_name="是否新品")
is_hot = models.BooleanField(default=False, verbose_name="是否热销")
#cases_desc = UEditorField(verbose_name=u"内容", imagePath="cases/images/", width=1000, height=300, filePath="cases/files/", default='')
cases_desc = models.TextField(max_length=10000, verbose_name="案例正文内容")
#cases_front_image = models.ImageField(upload_to="cases/images/", null=True, blank=True, verbose_name="封面图")
cases_front_image = models.CharField(max_length=200, verbose_name="封面图URL", blank=True)
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
@@ -58,19 +51,6 @@ class Cases(models.Model):
def __str__(self):
return self.name
class IndexAd(models.Model):
category = models.ForeignKey(CasesCategory, related_name='category',verbose_name="案例类目")
cases =models.ForeignKey(Cases, related_name='cases')
class Meta:
verbose_name = '首页案例类别广告'
verbose_name_plural = verbose_name
def __str__(self):
return self.cases.name
class CasesImage(models.Model):
"""
案例轮播图
+18 -32
View File
@@ -3,9 +3,7 @@ __author__ = 'HymanLu'
from rest_framework import serializers
from django.db.models import Q
from cases.models import Cases, CasesCategory, HotSearchWords, CasesImage, Banner
from cases.models import IndexAd
class CategorySerializer2(serializers.ModelSerializer):
@@ -26,13 +24,26 @@ class CasesImageSerializer(serializers.ModelSerializer):
model = CasesImage
fields = ("image", )
class CasesSerializer(serializers.ModelSerializer):
category = CategorySerializer()
images = CasesImageSerializer(many=True)
#category = CategorySerializer()
#images = CasesImageSerializer(many=True)
category_name = serializers.CharField(required=True, write_only=True, label="案例类别名称",
error_messages={
"required": "请输入案例类别名称",
"blank": "请输入案例类别名称"
})
def validate(self, attrs):
category_name = attrs["category_name"]
category = CasesCategory.objects.get(Q(name=category_name))
if not category:
raise serializers.ValidationError("该案例类别不存在")
attrs["category"] = category
del attrs["category_name"]
return attrs
class Meta:
model = Cases
fields = "__all__"
#fields = "__all__"
fields = ("category_name", "name", "cases_brief", "cases_desc", "cases_front_image")
class HotWordsSerializer(serializers.ModelSerializer):
@@ -44,29 +55,4 @@ class HotWordsSerializer(serializers.ModelSerializer):
class BannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
fields = "__all__"
class IndexCategorySerializer(serializers.ModelSerializer):
cases = serializers.SerializerMethodField()
sub_cat = CategorySerializer2(many=True)
ad_cases = serializers.SerializerMethodField()
def get_ad_cases(self, obj):
cases_json = {}
ad_cases = IndexAd.objects.filter(category_id=obj.id, )
if ad_cases:
case_ins = ad_cases[0].cases
cases_json = CasesSerializer(case_ins, many=False, context={'request': self.context['request']}).data
return cases_json
def get_cases(self, obj):
all_cases = Cases.objects.filter(Q(category_id=obj.id)|Q(category__parent_category_id=obj.id)|Q(category__parent_category__parent_category_id=obj.id))
cases_serializer = CasesSerializer(all_cases, many=True, context={'request': self.context['request']})
return cases_serializer.data
class Meta:
model = CasesCategory
fields = "__all__"
fields = "__all__"
+15 -18
View File
@@ -1,23 +1,20 @@
# -*- coding: utf-8 -*-
__author__ = 'HymanLu'
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import mixins
from rest_framework import generics
from rest_framework import filters
from rest_framework.pagination import PageNumberPagination
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from rest_framework.authentication import TokenAuthentication
from rest_framework.throttling import UserRateThrottle
from rest_framework_extensions.cache.mixins import CacheResponseMixin
from .models import Cases, CasesCategory, HotSearchWords, Banner
from .filters import CasesFilter
from .serializers import CasesSerializer, CategorySerializer, HotWordsSerializer, BannerSerializer
from .serializers import IndexCategorySerializer
from rest_framework import status
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticatedOrReadOnly
# Create your views here.
@@ -28,7 +25,7 @@ class CasesPagination(PageNumberPagination):
max_page_size = 100
class CasesListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
class CasesListViewSet(CacheResponseMixin, mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
案例列表页, 分页 搜索 过滤 排序
"""
@@ -36,7 +33,8 @@ class CasesListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.Retriev
queryset = Cases.objects.all()
serializer_class = CasesSerializer
pagination_class = CasesPagination
# authentication_classes = (TokenAuthentication, )
authentication_classes = (JSONWebTokenAuthentication, )
permission_classes = (IsAuthenticatedOrReadOnly,)
filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
filter_class = CasesFilter
search_fields = ('name', 'cases_brief', 'cases_desc')
@@ -49,6 +47,14 @@ class CasesListViewSet(CacheResponseMixin, mixins.ListModelMixin, mixins.Retriev
serializer = self.get_serializer(instance)
return Response(serializer.data)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = self.perform_create(serializer) # 注册成功,将SQL记录插入语句提交到数据库执行
re_dict = serializer.data
headers = self.get_success_headers(serializer.data)
return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
class CategoryViewset(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
list:
@@ -73,13 +79,4 @@ class BannerViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
获取案例轮播图列表
"""
queryset = Banner.objects.all().order_by("index")
serializer_class = BannerSerializer
class IndexCategoryViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
"""
首页案例分类数据
"""
queryset = CasesCategory.objects.filter(is_tab=True, name__in=["生鲜食品", "酒水饮料"])
serializer_class = IndexCategorySerializer
serializer_class = BannerSerializer
+2 -6
View File
@@ -2,13 +2,9 @@
__author__ = 'HymanLu'
import xadmin
from .models import UserFav, UserLeavingMessage
from .models import UserFav
class UserFavAdmin(object):
list_display = ['user', 'goods', "add_time"]
class UserLeavingMessageAdmin(object):
list_display = ['user', 'message_type', "message", "add_time"]
xadmin.site.register(UserFav, UserFavAdmin)
xadmin.site.register(UserLeavingMessage, UserLeavingMessageAdmin)
xadmin.site.register(UserFav, UserFavAdmin)
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.7 on 2017-11-14 17:39
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('user_operation', '0002_auto_20171021_2051'),
]
operations = [
migrations.RemoveField(
model_name='userleavingmessage',
name='user',
),
migrations.DeleteModel(
name='UserLeavingMessage',
),
]
+1 -28
View File
@@ -25,31 +25,4 @@ class UserFav(models.Model):
unique_together = ("user", "goods")
def __str__(self):
return self.user.username
class UserLeavingMessage(models.Model):
"""
用户留言
"""
MESSAGE_CHOICES = (
(1, "留言"),
(2, "投诉"),
(3, "询问"),
(4, "售后"),
(5, "求购")
)
user = models.ForeignKey(User, verbose_name="用户")
message_type = models.IntegerField(default=1, choices=MESSAGE_CHOICES, verbose_name="留言类型",
help_text=u"留言类型: 1(留言),2(投诉),3(询问),4(售后),5(求购)")
subject = models.CharField(max_length=100, default="", verbose_name="主题")
message = models.TextField(default="", verbose_name="留言内容", help_text="留言内容")
file = models.FileField(upload_to="message/images/", verbose_name="上传的文件", help_text="上传的文件")
add_time = models.DateTimeField(default=datetime.now, verbose_name="添加时间")
class Meta:
verbose_name = "用户留言"
verbose_name_plural = verbose_name
def __str__(self):
return self.subject
return self.user.username
+1 -12
View File
@@ -5,7 +5,6 @@ from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from .models import UserFav
from .models import UserLeavingMessage
from cases.serializers import CasesSerializer
@@ -32,14 +31,4 @@ class UserFavSerializer(serializers.ModelSerializer):
)
]
fields = ("user", "goods", "id")
class LeavingMessageSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
add_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M')
class Meta:
model = UserLeavingMessage
fields = ("user", "message_type", "subject", "message", "file", "id" ,"add_time")
fields = ("user", "goods", "id")
+3 -23
View File
@@ -6,10 +6,9 @@ from rest_framework import mixins
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.authentication import SessionAuthentication
from .models import UserFav, UserLeavingMessage
from .models import UserFav
from utils.permissions import IsOwnerOrReadOnly
from .serializers import UserFavSerializer, UserFavDetailSerializer, LeavingMessageSerializer
from .serializers import UserFavSerializer, UserFavDetailSerializer
class UserFavViewset(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin,
@@ -41,23 +40,4 @@ class UserFavViewset(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.Retr
elif self.action == "create":
return UserFavSerializer
return UserFavSerializer
class LeavingMessageViewset(mixins.ListModelMixin, mixins.DestroyModelMixin, mixins.CreateModelMixin,
viewsets.GenericViewSet):
"""
list:
获取用户留言
create:
添加留言
delete:
删除留言功能
"""
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
serializer_class = LeavingMessageSerializer
def get_queryset(self):
return UserLeavingMessage.objects.filter(user=self.request.user)
return UserFavSerializer
+1 -2
View File
@@ -39,5 +39,4 @@ class SmsVerifyCode(models.Model):
return self.code
from django.contrib.auth import get_user_model
User = get_user_model()
User = get_user_model(
+1 -1
View File
@@ -98,4 +98,4 @@ class UserRegSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("username", "code", "mobile", "password", "picture_token") # 返回attrs中的这些值作为最终序列化的结果
fields = ("username", "code", "mobile", "password", "picture_token") # 返回attrs中的这些值作为最终序列化的结果
-3
View File
@@ -22,7 +22,6 @@ from .models import SmsVerifyCode
from rest_framework.views import APIView
from captcha.models import CaptchaStore
from captcha.helpers import captcha_image_url
import json
'''
在setting.py文件中指定AUTHENTICATION_BACKENDS为CustomBackend即可生效
@@ -137,8 +136,6 @@ class UserViewset(CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveMode
payload = jwt_payload_handler(user)
re_dict["jwt_token"] = jwt_encode_handler(payload)
re_dict["name"] = user.name if user.name else user.username
headers = self.get_success_headers(serializer.data)
return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
+49
View File
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
__author__ = 'HymanLu'
from rest_framework.response import Response
from rest_framework import status
from rest_framework import views
import os
from APP_Inventor_case_base.settings import REQUEST_HOST
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class UploadFileView(views.APIView):
"""
上传文件接口
"""
authentication_classes = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticatedOrReadOnly,)
dir_name_to_valid_file_extension = {
'case_cover_picture': set(['bmp', 'jpg', 'jpeg', 'png', 'gif']),
'rich_text_picture': set(['bmp', 'jpg', 'jpeg', 'png', 'gif'])
}
def post(self, request, mediaDir=''):
error_reason_dict = {}
if len(mediaDir) == 0:
error_reason_dict['detail'] = '请传输上传目录的URL参数'
return Response(error_reason_dict, status=status.HTTP_400_BAD_REQUEST)
if mediaDir not in self.dir_name_to_valid_file_extension:
error_reason_dict['detail'] = '请输入合法的上传目录URL参数'
return Response(error_reason_dict, status=status.HTTP_400_BAD_REQUEST)
file = request.FILES.get('file')
fileName = file.name
file_extension = os.path.splitext(fileName)[1][1:]
if file_extension not in self.dir_name_to_valid_file_extension[mediaDir]:
error_reason_dict['detail'] = '不支持该文件类型'
return Response(error_reason_dict, status=status.HTTP_400_BAD_REQUEST)
baseDir = os.path.dirname(os.path.abspath(__name__))
userName = request.user.username
localFileDir = os.path.join(baseDir, 'media', mediaDir, userName)
if not os.path.exists(localFileDir):
os.makedirs(localFileDir)
localFileName = os.path.join(localFileDir, fileName)
requestURL = REQUEST_HOST + '/media/' + mediaDir + '/' + userName + '/' + fileName
with open(localFileName, 'wb') as fout:
for chrunk in file.chunks():
fout.write(chrunk)
return_data = { 'location' : requestURL }
return Response(return_data, status=status.HTTP_201_CREATED)
Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+253
View File
@@ -0,0 +1,253 @@
tinymce.addI18n('zh_CN',{
"Redo": "\u91cd\u590d",
"Undo": "\u64a4\u6d88",
"Cut": "\u526a\u5207",
"Copy": "\u590d\u5236",
"Paste": "\u7c98\u8d34",
"Select all": "\u5168\u9009",
"New document": "\u65b0\u6587\u6863",
"Ok": "\u786e\u5b9a",
"Cancel": "\u53d6\u6d88",
"Visual aids": "\u7f51\u683c\u7ebf",
"Bold": "\u7c97\u4f53",
"Italic": "\u659c\u4f53",
"Underline": "\u4e0b\u5212\u7ebf",
"Strikethrough": "\u5220\u9664\u7ebf",
"Superscript": "\u4e0a\u6807",
"Subscript": "\u4e0b\u6807",
"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Align left": "\u5de6\u5bf9\u9f50",
"Align center": "\u5c45\u4e2d",
"Align right": "\u53f3\u5bf9\u9f50",
"Justify": "\u4e24\u7aef\u5bf9\u9f50",
"Bullet list": "\u9879\u76ee\u7b26\u53f7",
"Numbered list": "\u7f16\u53f7\u5217\u8868",
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
"Close": "\u5173\u95ed",
"Formats": "\u683c\u5f0f",
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u5bf9\u526a\u8d34\u677f\u7684\u8bbf\u95ee\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u952e\u8fdb\u884c\u590d\u5236\u7c98\u8d34\u3002",
"Headers": "\u6807\u9898",
"Header 1": "\u6807\u98981",
"Header 2": "\u6807\u98982",
"Header 3": "\u6807\u98983",
"Header 4": "\u6807\u98984",
"Header 5": "\u6807\u98985",
"Header 6": "\u6807\u98986",
"Headings": "\u6807\u9898",
"Heading 1": "\u6807\u98981",
"Heading 2": "\u6807\u98982",
"Heading 3": "\u6807\u98983",
"Heading 4": "\u6807\u98984",
"Heading 5": "\u6807\u98985",
"Heading 6": "\u6807\u98986",
"Div": "Div\u533a\u5757",
"Pre": "\u9884\u683c\u5f0f\u6587\u672c",
"Code": "\u4ee3\u7801",
"Paragraph": "\u6bb5\u843d",
"Blockquote": "\u5f15\u7528",
"Inline": "\u6587\u672c",
"Blocks": "\u533a\u5757",
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
"Font Family": "\u5b57\u4f53",
"Font Sizes": "\u5b57\u53f7",
"Class": "Class",
"Browse for an image": "Browse for an image",
"OR": "OR",
"Drop an image here": "Drop an image here",
"Upload": "\u4e0a\u4f20",
"Default": "\u9ed8\u8ba4",
"Circle": "\u7a7a\u5fc3\u5706",
"Disc": "\u5b9e\u5fc3\u5706",
"Square": "\u65b9\u5757",
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
"Anchor": "\u951a\u70b9",
"Name": "\u540d\u79f0",
"Id": "\u6807\u8bc6\u7b26",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
"Special character": "\u7279\u6b8a\u7b26\u53f7",
"Source code": "\u6e90\u4ee3\u7801",
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
"Language": "\u8bed\u8a00",
"Color": "\u989c\u8272",
"R": "R",
"G": "G",
"B": "B",
"Left to right": "\u4ece\u5de6\u5230\u53f3",
"Right to left": "\u4ece\u53f3\u5230\u5de6",
"Emoticons": "\u8868\u60c5",
"Document properties": "\u6587\u6863\u5c5e\u6027",
"Title": "\u6807\u9898",
"Keywords": "\u5173\u952e\u8bcd",
"Description": "\u63cf\u8ff0",
"Robots": "\u673a\u5668\u4eba",
"Author": "\u4f5c\u8005",
"Encoding": "\u7f16\u7801",
"Fullscreen": "\u5168\u5c4f",
"Action": "Action",
"Shortcut": "Shortcut",
"Help": "Help",
"Address": "Address",
"Focus to menubar": "Focus to menubar",
"Focus to toolbar": "Focus to toolbar",
"Focus to element path": "Focus to element path",
"Focus to contextual toolbar": "Focus to contextual toolbar",
"Insert link (if link plugin activated)": "Insert link (if link plugin activated)",
"Save (if save plugin activated)": "Save (if save plugin activated)",
"Find (if searchreplace plugin activated)": "Find (if searchreplace plugin activated)",
"Plugins installed ({0}):": "Plugins installed ({0}):",
"Premium plugins:": "Premium plugins:",
"Learn more...": "Learn more...",
"You are using {0}": "You are using {0}",
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
"Image description": "\u56fe\u7247\u63cf\u8ff0",
"Source": "\u5730\u5740",
"Dimensions": "\u5927\u5c0f",
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
"General": "\u666e\u901a",
"Advanced": "\u9ad8\u7ea7",
"Style": "\u6837\u5f0f",
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
"Border": "\u8fb9\u6846",
"Insert image": "\u63d2\u5165\u56fe\u7247",
"Image": "\u56fe\u7247",
"Image list": "\u56fe\u7247\u5217\u8868",
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
"Edit image": "\u7f16\u8f91\u56fe\u7247",
"Image options": "\u56fe\u7247\u9009\u9879",
"Zoom in": "\u653e\u5927",
"Zoom out": "\u7f29\u5c0f",
"Crop": "\u88c1\u526a",
"Resize": "\u8c03\u6574\u5927\u5c0f",
"Orientation": "\u65b9\u5411",
"Brightness": "\u4eae\u5ea6",
"Sharpen": "\u9510\u5316",
"Contrast": "\u5bf9\u6bd4\u5ea6",
"Color levels": "\u989c\u8272\u5c42\u6b21",
"Gamma": "\u4f3d\u9a6c\u503c",
"Invert": "\u53cd\u8f6c",
"Apply": "\u5e94\u7528",
"Back": "\u540e\u9000",
"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
"Insert link": "\u63d2\u5165\u94fe\u63a5",
"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
"Text to display": "\u663e\u793a\u6587\u5b57",
"Url": "\u5730\u5740",
"Target": "\u6253\u5f00\u65b9\u5f0f",
"None": "\u65e0",
"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
"Remove link": "\u5220\u9664\u94fe\u63a5",
"Anchors": "\u951a\u70b9",
"Link": "\u94fe\u63a5",
"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
"Link list": "Link list",
"Insert video": "\u63d2\u5165\u89c6\u9891",
"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
"Alternative source": "\u955c\u50cf",
"Poster": "\u5c01\u9762",
"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
"Embed": "\u5185\u5d4c",
"Media": "\u5a92\u4f53",
"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
"Page break": "\u5206\u9875\u7b26",
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
"Preview": "\u9884\u89c8",
"Print": "\u6253\u5370",
"Save": "\u4fdd\u5b58",
"Find": "\u67e5\u627e",
"Replace with": "\u66ff\u6362\u4e3a",
"Replace": "\u66ff\u6362",
"Replace all": "\u5168\u90e8\u66ff\u6362",
"Prev": "\u4e0a\u4e00\u4e2a",
"Next": "\u4e0b\u4e00\u4e2a",
"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
"Whole words": "\u5168\u5b57\u5339\u914d",
"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
"Ignore": "\u5ffd\u7565",
"Ignore all": "\u5168\u90e8\u5ffd\u7565",
"Finish": "\u5b8c\u6210",
"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
"Insert table": "\u63d2\u5165\u8868\u683c",
"Table properties": "\u8868\u683c\u5c5e\u6027",
"Delete table": "\u5220\u9664\u8868\u683c",
"Cell": "\u5355\u5143\u683c",
"Row": "\u884c",
"Column": "\u5217",
"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
"Delete row": "\u5220\u9664\u884c",
"Row properties": "\u884c\u5c5e\u6027",
"Cut row": "\u526a\u5207\u884c",
"Copy row": "\u590d\u5236\u884c",
"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
"Delete column": "\u5220\u9664\u5217",
"Cols": "\u5217",
"Rows": "\u884c",
"Width": "\u5bbd",
"Height": "\u9ad8",
"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
"Caption": "\u6807\u9898",
"Left": "\u5de6\u5bf9\u9f50",
"Center": "\u5c45\u4e2d",
"Right": "\u53f3\u5bf9\u9f50",
"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
"Scope": "\u8303\u56f4",
"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
"H Align": "\u6c34\u5e73\u5bf9\u9f50",
"V Align": "\u5782\u76f4\u5bf9\u9f50",
"Top": "\u9876\u90e8\u5bf9\u9f50",
"Middle": "\u5782\u76f4\u5c45\u4e2d",
"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
"Header cell": "\u8868\u5934\u5355\u5143\u683c",
"Row group": "\u884c\u7ec4",
"Column group": "\u5217\u7ec4",
"Row type": "\u884c\u7c7b\u578b",
"Header": "\u8868\u5934",
"Body": "\u8868\u4f53",
"Footer": "\u8868\u5c3e",
"Border color": "\u8fb9\u6846\u989c\u8272",
"Insert template": "\u63d2\u5165\u6a21\u677f",
"Templates": "\u6a21\u677f",
"Template": "Template",
"Text color": "\u6587\u5b57\u989c\u8272",
"Background color": "\u80cc\u666f\u8272",
"Custom...": "\u81ea\u5b9a\u4e49...",
"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
"No color": "\u65e0",
"Table of Contents": "\u5185\u5bb9\u5217\u8868",
"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
"Words: {0}": "\u5b57\u6570\uff1a{0}",
"File": "\u6587\u4ef6",
"Edit": "\u7f16\u8f91",
"Insert": "\u63d2\u5165",
"View": "\u89c6\u56fe",
"Format": "\u683c\u5f0f",
"Table": "\u8868\u683c",
"Tools": "\u5de5\u5177",
"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9"
});