├── .gitignore ├── .idea ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── think.iml ├── vcs.xml └── workspace.xml ├── .vscode └── settings.json ├── LICENSE.md ├── README.md ├── djapi ├── djapi │ ├── __init__.py │ ├── settings │ │ ├── __init__.py │ │ ├── base.py │ │ ├── local_.py │ │ └── settings.py │ ├── urls.py │ └── wsgi.py ├── gunicorn.conf.py ├── manage.py ├── passport │ ├── __init__.py │ ├── api.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_weixinusers_created.py │ │ └── __init__.py │ ├── models.py │ └── tests.py ├── requirements.txt ├── think │ ├── __init__.py │ ├── api.py │ ├── filter.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20171025_2311.py │ │ ├── 0003_auto_20171109_1123.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ └── tests.py └── tools │ ├── auth.py │ ├── helper.py │ └── rest_helper.py └── weixin ├── app.js ├── app.json ├── app.wxss ├── pages ├── detail │ ├── detail.js │ ├── detail.json │ ├── detail.wxml │ ├── detail.wxss │ └── share │ │ ├── share.js │ │ ├── share.json │ │ ├── share.wxml │ │ └── share.wxss ├── edit │ ├── edit.js │ ├── edit.json │ ├── edit.wxml │ ├── edit.wxss │ └── location │ │ ├── location.js │ │ ├── location.json │ │ ├── location.wxml │ │ └── location.wxss ├── index │ ├── earth │ │ ├── earth.js │ │ ├── earth.json │ │ ├── earth.wxml │ │ └── earth.wxss │ ├── index.js │ ├── index.wxml │ └── index.wxss └── logs │ ├── logs.js │ ├── logs.json │ ├── logs.wxml │ └── logs.wxss ├── static ├── image │ ├── add.png │ ├── delete.png │ ├── earth.png │ ├── edit.png │ ├── home.png │ ├── home_selected.png │ ├── index.png │ ├── index_selected.png │ ├── location.png │ ├── location_selected.png │ ├── save_pic.png │ └── think.png └── style │ └── weui.wxss └── utils ├── WxNotificationCenter.js ├── config.js ├── event-emitter.js ├── helper_business.js ├── qqmap-wx-jssdk.min.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | local.py -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/think.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | pyc 120 | id 121 | that 122 | 123 | 124 | 125 | 127 | 128 | 141 | 142 | 143 | 144 | 145 | true 146 | DEFINITION_ORDER 147 | 148 | 149 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 176 | 177 | 180 | 181 | 182 | 183 | 186 | 187 | 190 | 191 | 194 | 195 | 196 | 197 | 200 | 201 | 204 | 205 | 208 | 209 | 212 | 213 | 214 | 215 | 218 | 219 | 222 | 223 | 226 | 227 | 230 | 231 | 232 | 233 | 236 | 237 | 240 | 241 | 244 | 245 | 248 | 249 | 250 | 251 | 254 | 255 | 258 | 259 | 262 | 263 | 266 | 267 | 270 | 271 | 272 | 273 | 276 | 277 | 280 | 281 | 284 | 285 | 288 | 289 | 292 | 293 | 296 | 297 | 298 | 299 | 302 | 303 | 306 | 307 | 310 | 311 | 312 | 313 | 316 | 317 | 320 | 321 | 324 | 325 | 328 | 329 | 330 | 331 | 334 | 335 | 338 | 339 | 342 | 343 | 346 | 347 | 348 | 349 | 352 | 353 | 356 | 357 | 360 | 361 | 364 | 365 | 366 | 367 | 370 | 371 | 374 | 375 | 378 | 379 | 382 | 383 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 408 | 409 | 410 | 412 | 413 | 414 | 415 | 1511179271814 416 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 451 | 452 | 454 | 455 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false 3 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 yongxinz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 欢迎关注我的微信公众号:AlwaysBeta,更多精彩内容等你来。 2 | 3 | ![](http://ww1.sinaimg.cn/large/0061a0TTgy1gaqr087j9xj3076076wex.jpg) 4 | 5 | # think 6 | 7 | 记录位置和想法,以时间线形式来展示的微信小程序 8 | 9 | ![](http://ohl540wt2.bkt.clouddn.com/2017-11-20.gif) 10 | 11 | ## 功能 12 | 目前支持功能:新加、修改想法,生成想法详情卡片并保存到相册,生成足迹卡片并保存到相册 13 | 14 | ## 环境 15 | Python 版本为 python3.6,后端使用:Django + restframework
16 | 小程序基础库版本 1.5.3 17 | 18 | ## 安装 19 | 1、新建虚拟环境 20 | >mkvirtualenv think --python=python3 21 | 22 | 2、安装依赖包 23 | >cd djapi
24 | >pip install -r requestments.txt 25 | 26 | 3、配置 APPID 和 APPsecret 27 | >cp djapi/djapi/settings/local_.py djapi/djapi/settings/local.py 28 | 29 | 然后将其中的 id 和 key 替换成你自己的 30 | 31 | >WEIXIN = { 32 | > 'url': 'https://api.weixin.qq.com', 33 | > 'id': 'your appid', 34 | > 'key': 'your appsecret' 35 | >} 36 | 37 | 4、初始化表 38 | >python manage.py migrate 39 | 40 | 5、Run,并运行小程序 41 | >python manage.py runserver 0.0.0.0:8810 42 | 43 | ## 抢先体验 44 | ![](http://ohl540wt2.bkt.clouddn.com/think.jpg) -------------------------------------------------------------------------------- /djapi/djapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/think/59681c91afd808b1d6f9b85b3da2af95a9b41418/djapi/djapi/__init__.py -------------------------------------------------------------------------------- /djapi/djapi/settings/__init__.py: -------------------------------------------------------------------------------- 1 | try: 2 | from .local import * 3 | except ImportError: 4 | from .base import * 5 | -------------------------------------------------------------------------------- /djapi/djapi/settings/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from .settings import * 5 | from os.path import join, abspath, dirname 6 | 7 | # make root path 8 | here = lambda *x: join(abspath(dirname(__file__)), *x) 9 | PROJECT_ROOT = here("..", "..") 10 | root = lambda *x: join(abspath(PROJECT_ROOT), *x) 11 | 12 | DEBUG = False 13 | 14 | TIME_ZONE = 'Asia/Shanghai' 15 | LANGUAGE_CODE = 'zh-hans' 16 | LOGIN_URL = "/passport/login/" 17 | LOGOUT_URL = "/passport/logout/" 18 | LOGIN_REDIRECT_URL = "/passport/" 19 | 20 | ALLOWED_HOSTS = [ 21 | '127.0.0.1', 22 | 'think.naturez.cn' 23 | ] 24 | 25 | INTERNAL_IPS = ['127.0.0.1', 'think.naturez.cn'] 26 | INSTALLED_APPS += [ 27 | 'think', 28 | 'passport' 29 | ] 30 | 31 | CAPTCHA_BACKGROUND_COLOR = "#eee" 32 | CAPTCHA_CHALLENGE_FUNCT = 'tools.helper.random_digit_challenge' 33 | 34 | REST_FRAMEWORK = { 35 | 'DEFAULT_AUTHENTICATION_CLASSES': ('tools.auth.YMAuthentication',), 36 | 'DEFAULT_PAGINATION_CLASS': 'tools.rest_helper.YMPagination', 37 | 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',), 38 | } 39 | 40 | # for test 41 | TEST_RUNNER = 'test_runner.YmRunner' 42 | -------------------------------------------------------------------------------- /djapi/djapi/settings/local_.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from .base import * 5 | 6 | DEBUG = True 7 | 8 | DATABASES = { 9 | 'default': { 10 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 11 | 'NAME': 'think', 12 | } 13 | } 14 | 15 | INSTALLED_APPS += [ 16 | 'rest_framework', 17 | # 'debug_toolbar', 18 | ] 19 | 20 | WEIXIN = { 21 | 'url': 'https://api.weixin.qq.com', 22 | 'id': 'your appid', 23 | 'key': 'your appsecret', 24 | } 25 | -------------------------------------------------------------------------------- /djapi/djapi/settings/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for djapi project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'e*e397y)u@tb7a*w$7)28@krj1a6ul^mc2n+37f^g8y^+zl*#7' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['192.168.1.11'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | ] 41 | 42 | MIDDLEWARE = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ] 51 | 52 | ROOT_URLCONF = 'djapi.urls' 53 | 54 | TEMPLATES = [ 55 | { 56 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 57 | 'DIRS': [], 58 | 'APP_DIRS': True, 59 | 'OPTIONS': { 60 | 'context_processors': [ 61 | 'django.template.context_processors.debug', 62 | 'django.template.context_processors.request', 63 | 'django.contrib.auth.context_processors.auth', 64 | 'django.contrib.messages.context_processors.messages', 65 | ], 66 | }, 67 | }, 68 | ] 69 | 70 | WSGI_APPLICATION = 'djapi.wsgi.application' 71 | 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 75 | 76 | DATABASES = { 77 | 'default': { 78 | 'ENGINE': 'django.db.backends.sqlite3', 79 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 80 | } 81 | } 82 | 83 | 84 | # Password validation 85 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 86 | 87 | AUTH_PASSWORD_VALIDATORS = [ 88 | { 89 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 90 | }, 91 | { 92 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 93 | }, 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 99 | }, 100 | ] 101 | 102 | 103 | # Internationalization 104 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 105 | 106 | LANGUAGE_CODE = 'en-us' 107 | 108 | TIME_ZONE = 'UTC' 109 | 110 | USE_I18N = True 111 | 112 | USE_L10N = True 113 | 114 | USE_TZ = True 115 | 116 | 117 | # Static files (CSS, JavaScript, Images) 118 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 119 | 120 | STATIC_URL = '/static/' 121 | -------------------------------------------------------------------------------- /djapi/djapi/urls.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """djapi URL Configuration 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/1.11/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.conf.urls import url, include 15 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 16 | """ 17 | from django.conf.urls import url, include 18 | from rest_framework import routers 19 | 20 | from passport.api import WeixinUserViewSet 21 | from think.api import ThinkViewSet 22 | 23 | router = routers.DefaultRouter() 24 | 25 | # 微信登录 26 | router.register(r'passport/wx', WeixinUserViewSet, base_name='passport_weixin') 27 | router.register(r'think', ThinkViewSet, base_name='think') 28 | 29 | urlpatterns = [ 30 | url(r'^api/', include(router.urls)), 31 | ] 32 | -------------------------------------------------------------------------------- /djapi/djapi/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for djapi project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djapi.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /djapi/gunicorn.conf.py: -------------------------------------------------------------------------------- 1 | workers = 3 2 | worker_class = "gevent" 3 | bind = "127.0.0.1:8810" 4 | -------------------------------------------------------------------------------- /djapi/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djapi.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /djapi/passport/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/think/59681c91afd808b1d6f9b85b3da2af95a9b41418/djapi/passport/__init__.py -------------------------------------------------------------------------------- /djapi/passport/api.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import uuid 3 | import requests 4 | 5 | from django.conf import settings 6 | from rest_framework.decorators import list_route 7 | from rest_framework import viewsets 8 | from rest_framework.response import Response 9 | 10 | from passport.models import WeixinUsers 11 | from tools.helper import Dict2obj 12 | 13 | 14 | class WeixinUserViewSet(viewsets.ViewSet): 15 | @list_route(methods=['get'], authentication_classes=()) 16 | def login(self, request): 17 | # 通过微信code获取用户openid 18 | conf = Dict2obj(settings.WEIXIN) 19 | url = conf.url + "/sns/jscode2session" 20 | js_code = request.query_params.get('code', '') 21 | params = dict(appid=conf.id, secret=conf.key, js_code=js_code, grant_type="authorization_code") 22 | rq = requests.get(url, params=params).json() 23 | 24 | if rq.get('errcode'): 25 | return Response(dict(status=False, msg=rq)) 26 | 27 | # 更新该微信号(openid)的sid和skey 28 | wx_user, is_new = WeixinUsers.objects.update_or_create(openid=rq.get('openid'), is_del=False, defaults={ 29 | 'session_key': rq.get('session_key'), 30 | 'unionid': rq.get('unionid', ''), 31 | 'sid': uuid.uuid4(), 32 | }) 33 | 34 | return Response(dict(status=True, sid=wx_user.sid)) 35 | 36 | @list_route(methods=['post'], authentication_classes=()) 37 | def join2wx(self, request): 38 | # 检查微信登录是否成功 39 | sid = request.META.get('HTTP_AUTHORIZATION', 'whoareyou') 40 | try: 41 | wx_user = WeixinUsers.objects.get(sid=sid, is_del=False) 42 | except: 43 | return Response({'status': False, 'msg': u'微信验证失败,请重新授权'}) 44 | -------------------------------------------------------------------------------- /djapi/passport/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-10-25 14:47 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import uuid 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='weixinUsers', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('openid', models.CharField(max_length=50, verbose_name='openid')), 22 | ('unionid', models.CharField(default='', max_length=50, verbose_name='uuid')), 23 | ('session_key', models.CharField(default='', max_length=50)), 24 | ('sid', models.UUIDField(default=uuid.uuid1)), 25 | ('is_del', models.BooleanField(default=False)), 26 | ], 27 | options={ 28 | 'ordering': ['-pk'], 29 | 'get_latest_by': 'pk', 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /djapi/passport/migrations/0002_weixinusers_created.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-11-09 03:23 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.utils.timezone 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('passport', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='weixinusers', 18 | name='created', 19 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='注册时间'), 20 | preserve_default=False, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /djapi/passport/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/think/59681c91afd808b1d6f9b85b3da2af95a9b41418/djapi/passport/migrations/__init__.py -------------------------------------------------------------------------------- /djapi/passport/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import uuid 3 | 4 | from django.db import models 5 | 6 | 7 | class WeixinUsers(models.Model): 8 | openid = models.CharField(u'openid', max_length=50) 9 | unionid = models.CharField(u'uuid', max_length=50, default='') 10 | session_key = models.CharField(max_length=50, default='') 11 | sid = models.UUIDField(default=uuid.uuid1) 12 | is_del = models.BooleanField(default=False) 13 | created = models.DateTimeField(u"注册时间", auto_now_add=True) 14 | 15 | class Meta: 16 | ordering = ['-pk'] 17 | get_latest_by = "pk" 18 | -------------------------------------------------------------------------------- /djapi/passport/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /djapi/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==1.11.1 2 | djangorestframework==3.7.1 3 | psycopg2==2.7.3.2 4 | requests==2.18.4 5 | django-filter==1.1.0 6 | python-dateutil==2.6.1 -------------------------------------------------------------------------------- /djapi/think/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/think/59681c91afd808b1d6f9b85b3da2af95a9b41418/djapi/think/__init__.py -------------------------------------------------------------------------------- /djapi/think/api.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import datetime 5 | 6 | from rest_framework import viewsets 7 | from rest_framework.response import Response 8 | from rest_framework.decorators import list_route 9 | 10 | from .models import Think 11 | from .serializers import ThinkSerializer 12 | from .filter import ThinkFilter 13 | 14 | 15 | class ThinkViewSet(viewsets.ModelViewSet): 16 | queryset = Think.objects.all() 17 | serializer_class = ThinkSerializer 18 | filter_class = ThinkFilter 19 | 20 | def get_queryset(self): 21 | queryset = Think.objects.filter(weixin=self.request.user) 22 | 23 | return queryset 24 | 25 | def perform_create(self, serializer): 26 | update_dict = {} 27 | 28 | update_dict['weixin'] = self.request.user 29 | update_dict['created'] = datetime.datetime.now() 30 | serializer.save(**update_dict) 31 | 32 | @list_route(methods=['get']) 33 | def earth(self, request): 34 | now = datetime.datetime.now() 35 | year = now.strftime("%Y") 36 | obj = Think.objects.filter(weixin=request.user, created__startswith=year) 37 | 38 | think_count = obj.count() 39 | nation_count = obj.exclude(nation='').distinct('nation').order_by('nation').count() 40 | city_count = obj.exclude(city='').distinct('city').order_by('city').count() 41 | 42 | return Response( 43 | data={'year': year, 'think_count': think_count, 'nation_count': nation_count, 'city_count': city_count, 44 | 'now': now.strftime("%Y-%m-%d %H:%M:%S")}) 45 | -------------------------------------------------------------------------------- /djapi/think/filter.py: -------------------------------------------------------------------------------- 1 | import django_filters 2 | from .models import Think 3 | 4 | 5 | class ThinkFilter(django_filters.rest_framework.FilterSet): 6 | 7 | class Meta: 8 | model = Think 9 | fields = ['id', ] 10 | -------------------------------------------------------------------------------- /djapi/think/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-10-25 14:47 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('passport', '0001_initial'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='Think', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('think', models.TextField(max_length=1000, verbose_name='想法')), 23 | ('nation', models.CharField(blank=True, max_length=20, null=True, verbose_name='国家')), 24 | ('province', models.CharField(blank=True, max_length=20, null=True, verbose_name='省')), 25 | ('city', models.CharField(blank=True, max_length=20, null=True, verbose_name='城市')), 26 | ('address', models.CharField(blank=True, max_length=50, null=True, verbose_name='地址')), 27 | ('created', models.DateTimeField(verbose_name='创建时间')), 28 | ('weixin', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passport.weixinUsers')), 29 | ], 30 | options={ 31 | 'ordering': ['-pk'], 32 | }, 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /djapi/think/migrations/0002_auto_20171025_2311.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-10-25 15:11 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('think', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='think', 18 | name='weixin', 19 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passport.WeixinUsers'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /djapi/think/migrations/0003_auto_20171109_1123.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.6 on 2017-11-09 03:23 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('think', '0002_auto_20171025_2311'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AlterField( 17 | model_name='think', 18 | name='weixin', 19 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='passport.WeixinUsers'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /djapi/think/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yongxinz/think/59681c91afd808b1d6f9b85b3da2af95a9b41418/djapi/think/migrations/__init__.py -------------------------------------------------------------------------------- /djapi/think/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from dateutil import tz 4 | 5 | from django.conf import settings 6 | from django.db import models 7 | from passport.models import WeixinUsers 8 | 9 | 10 | class Think(models.Model): 11 | """想法""" 12 | weixin = models.ForeignKey(WeixinUsers) 13 | think = models.TextField(u'想法', max_length=1000) 14 | nation = models.CharField(u"国家", max_length=20, null=True, blank=True) 15 | province = models.CharField(u"省", max_length=20, null=True, blank=True) 16 | city = models.CharField(u"城市", max_length=20, null=True, blank=True) 17 | address = models.CharField(u"地址", max_length=50, null=True, blank=True) 18 | created = models.DateTimeField(u"创建时间") 19 | 20 | def get_created(self): 21 | return self.created.astimezone(tz.gettz(settings.TIME_ZONE)).strftime('%Y-%m-%d %H:%M:%S') 22 | 23 | def get_day(self): 24 | return self.created.day 25 | 26 | def get_month(self): 27 | return self.created.month 28 | 29 | class Meta: 30 | ordering = ['-pk'] 31 | 32 | -------------------------------------------------------------------------------- /djapi/think/serializers.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from rest_framework import serializers 3 | 4 | from .models import Think 5 | 6 | 7 | class ThinkSerializer(serializers.ModelSerializer): 8 | created_display = serializers.ReadOnlyField(source='get_created') 9 | month = serializers.ReadOnlyField(source='get_month') 10 | day = serializers.ReadOnlyField(source='get_day') 11 | 12 | class Meta: 13 | model = Think 14 | exclude = ('weixin', 'created') 15 | -------------------------------------------------------------------------------- /djapi/think/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /djapi/tools/auth.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from rest_framework import authentication, exceptions 3 | 4 | from passport.models import WeixinUsers 5 | from tools.helper import Dict2obj 6 | 7 | 8 | class YMAuthentication(authentication.BaseAuthentication): 9 | def authenticate(self, request): 10 | hash_key = request.META.get('HTTP_AUTHORIZATION', '123abc') 11 | try: 12 | user = WeixinUsers.objects.get(sid=hash_key, is_del=False) 13 | except: 14 | raise exceptions.AuthenticationFailed({ 15 | 'status': False, 16 | 'status_code': 403010, 17 | }) 18 | 19 | return user, Dict2obj({ 20 | 'user': user, 21 | }) 22 | -------------------------------------------------------------------------------- /djapi/tools/helper.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | import requests 4 | import datetime 5 | import re 6 | import random 7 | from decimal import Decimal 8 | 9 | from django.conf import settings 10 | 11 | 12 | class Helper(object): 13 | @classmethod 14 | def get_safe_now(self): 15 | try: 16 | from django.utils.timezone import utc 17 | if settings.USE_TZ: 18 | return datetime.datetime.utcnow().replace(tzinfo=utc) 19 | except: 20 | pass 21 | 22 | return datetime.datetime.now() 23 | 24 | @classmethod 25 | def mk_random(self, num=8, ram_type='str'): 26 | ram_int = '1234567890' 27 | ram_str = 'abcdefghigklmnopqrstuvwxyz1234567890' 28 | 29 | if ram_type != 'str': 30 | ram_str = ram_int 31 | 32 | return "".join(random.sample(ram_str, num)) 33 | 34 | @classmethod 35 | def password_format_check(self, password): 36 | result = {"status": True} 37 | # 至少8位 38 | if len(password) < 8: 39 | result.update({"status": False, 'msg': "密码过于简单,密码至少8位"}) 40 | # 至少一个数字 41 | if not re.match(r'.*[0-9]+', password): 42 | result.update({"status": False, 'msg': "密码过于简单,至少包括一个数字"}) 43 | # 至少一个小写字母 44 | if not re.match(r'.*[a-zA-Z]+', password): 45 | result.update({"status": False, 'msg': "密码过于简单,至少包括一个字母"}) 46 | 47 | return result 48 | 49 | @classmethod 50 | def mobile_format_check(self, mobile): 51 | result = {"status": True} 52 | # 至少11位 53 | if len(mobile) != 11: 54 | result.update({"status": False, 'msg': u"请填写11位手机号码"}) 55 | # 全部数字 56 | if not re.match(r'^1[0-9]{10}', mobile): 57 | result.update({"status": False, 'msg': u"手机格式不对,请填写11位手机号码"}) 58 | 59 | return result 60 | 61 | 62 | class Dict2obj(object): 63 | def __init__(self, dict_data): 64 | self.__dict__ = dict_data 65 | 66 | def __str__(self): 67 | return str(self.__dict__) 68 | 69 | def __getattr__(self, item): 70 | return self.__dict__.get(item) 71 | 72 | 73 | def random_digit_challenge(): 74 | ret = u'' 75 | for i in range(4): 76 | ret += str(random.randint(0, 9)) 77 | return ret, ret 78 | 79 | 80 | class YMapi(object): 81 | # YMapi.get('adp', 'erp').json() 82 | 83 | @classmethod 84 | def get(self, api=' ', service='erp', params=None): 85 | host = settings.API_SRV.get(service).get('host') 86 | api = settings.API_SRV.get(service).get('api').get(api) 87 | auth = settings.API_SRV.get(service).get('authorization') 88 | key = settings.API_SRV.get(service).get('key') 89 | if auth: 90 | r = requests.get(url=host + api, headers={'AUTHORIZATION': auth}, params=params) 91 | else: 92 | r = requests.get(url=host + api + '?key=' + key, params=params) 93 | return r 94 | 95 | @classmethod 96 | def post(self, api='url', service='erp', params=None, data=None, pk=''): 97 | host = settings.API_SRV.get(service).get('host') 98 | api = settings.API_SRV.get(service).get('api').get(api) 99 | auth = settings.API_SRV.get(service).get('authorization') 100 | key = settings.API_SRV.get(service).get('key') 101 | pk = (pk + '/') if pk else '' 102 | if auth: 103 | r = requests.post(url=host + api + pk, headers={'AUTHORIZATION': auth}, params=params, json=data) 104 | else: 105 | r = requests.post(url=host + api + pk + '?key=' + key, params=params, json=data) 106 | return r 107 | 108 | @classmethod 109 | def put(self, api='url', service='erp', params=None, data=None, pk=''): 110 | host = settings.API_SRV.get(service).get('host') 111 | api = settings.API_SRV.get(service).get('api').get(api) 112 | auth = settings.API_SRV.get(service).get('authorization') 113 | key = settings.API_SRV.get(service).get('key') 114 | pk = (pk + '/') if pk else '' 115 | if auth: 116 | r = requests.put(url=host + api + pk, headers={'AUTHORIZATION': auth}, params=params, json=data) 117 | else: 118 | r = requests.put(url=host + api + pk + '?key=' + key, params=params, json=data) 119 | return r 120 | 121 | @classmethod 122 | def patch(self, api='url', service='erp', params=None, data=None, pk=''): 123 | host = settings.API_SRV.get(service).get('host') 124 | api = settings.API_SRV.get(service).get('api').get(api) 125 | auth = settings.API_SRV.get(service).get('authorization') 126 | key = settings.API_SRV.get(service).get('key') 127 | pk = (pk + '/') if pk else '' 128 | if auth: 129 | r = requests.patch(url=host + api + pk, headers={'AUTHORIZATION': auth}, params=params, json=data) 130 | else: 131 | r = requests.patch(url=host + api + pk + '?key=' + key, params=params, json=data) 132 | return r 133 | 134 | @classmethod 135 | def delete(self, api='url', service='erp', params=None, pk=''): 136 | host = settings.API_SRV.get(service).get('host') 137 | api = settings.API_SRV.get(service).get('api').get(api) 138 | auth = settings.API_SRV.get(service).get('authorization') 139 | key = settings.API_SRV.get(service).get('key') 140 | pk = (pk + '/') if pk else '' 141 | if auth: 142 | r = requests.delete(url=host + api + pk, headers={'AUTHORIZATION': auth}, params=params) 143 | else: 144 | r = requests.delete(url=host + api + pk + '?key=' + key, params=params) 145 | return r 146 | 147 | 148 | def decimal_round(value, num=2): 149 | save_num = '{:.' + str(num) + 'f}' 150 | return save_num.format(Decimal(str(value))) 151 | -------------------------------------------------------------------------------- /djapi/tools/rest_helper.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from rest_framework.pagination import PageNumberPagination 3 | 4 | 5 | class YMPagination(PageNumberPagination): 6 | page_size = 15 7 | page_size_query_param = 'size' 8 | max_page_size = 1000 9 | -------------------------------------------------------------------------------- /weixin/app.js: -------------------------------------------------------------------------------- 1 | import helperB from "./utils/helper_business"; 2 | import config from "./utils/config"; 3 | 4 | App({ 5 | helper: helperB, 6 | config: config, 7 | 8 | onLaunch: function () { 9 | // 微信登录,获取用户code 10 | helperB.wxPromisify(wx.login)().then(function (res) { 11 | // 登录后端, wx code -> dj sid 12 | helperB.getApi(config.api.login, { code: res.code }).then(function (res) { 13 | config.gData.userSid = res.data.sid; 14 | config.emitter.emit('userSid'); 15 | }); 16 | }).catch(function (res) { 17 | console.error(res.errMsg) 18 | }); 19 | } 20 | // globalData: { 21 | // userInfo: null 22 | // } 23 | }); 24 | -------------------------------------------------------------------------------- /weixin/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/index/earth/earth", 5 | "pages/detail/detail", 6 | "pages/detail/share/share", 7 | "pages/edit/edit", 8 | "pages/edit/location/location", 9 | "pages/logs/logs" 10 | ], 11 | "window": { 12 | "backgroundTextStyle": "light", 13 | "navigationBarBackgroundColor": "#fff", 14 | "navigationBarTitleText": "灵感乍现", 15 | "navigationBarTextStyle": "black" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /weixin/app.wxss: -------------------------------------------------------------------------------- 1 | @import 'static/style/weui.wxss'; 2 | 3 | page { 4 | background-color: #f0f0f0; 5 | font-size: 16px; 6 | font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; 7 | } 8 | 9 | .page__hd { 10 | padding: 40px; 11 | } 12 | .page__bd { 13 | padding-bottom: 40px; 14 | } 15 | .page__bd_spacing { 16 | padding-left: 15px; 17 | padding-right: 15px; 18 | } 19 | 20 | .page__ft{ 21 | padding-bottom: 10px; 22 | text-align: center; 23 | } 24 | 25 | .page__title { 26 | text-align: left; 27 | font-size: 20px; 28 | font-weight: 400; 29 | } 30 | 31 | .page__desc { 32 | margin-top: 5px; 33 | color: #888888; 34 | text-align: left; 35 | font-size: 14px; 36 | } 37 | -------------------------------------------------------------------------------- /weixin/pages/detail/detail.js: -------------------------------------------------------------------------------- 1 | const app = getApp(); 2 | 3 | Page({ 4 | data: { 5 | windowWidth: 0, 6 | windowHeight: 0 7 | }, 8 | 9 | onLoad: function (options) { 10 | let that = this; 11 | that.setData({ options: options }); 12 | wx.getSystemInfo({ 13 | success: function (res) { 14 | that.setData({ 15 | windowWidth: res.windowWidth, 16 | windowHeight: res.windowHeight 17 | }); 18 | } 19 | }); 20 | }, 21 | 22 | onShow: function () { 23 | app.helper.waitUserSid(this.getApiData) 24 | }, 25 | 26 | getApiData: function () { 27 | let that = this; 28 | 29 | wx.showLoading({ title: '加载中...' }); 30 | app.helper.getApi(app.config.api.think, that.data.options).then(function (res) { 31 | that.setData({ id: res.data.results[0].id, think: res.data.results[0].think, address: res.data.results[0].address, created: res.data.results[0].created_display }); 32 | }).then(function () { 33 | wx.hideLoading() 34 | }); 35 | }, 36 | 37 | thinkEdit: function(e) { 38 | let id = e.currentTarget.dataset.id; 39 | wx.navigateTo({ url: '../edit/edit?id=' + id }) 40 | }, 41 | 42 | thinkPic: function (e) { 43 | let id = e.currentTarget.dataset.id; 44 | wx.navigateTo({ url: './share/share?id=' + id }) 45 | } 46 | }); -------------------------------------------------------------------------------- /weixin/pages/detail/detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "想法" 3 | } -------------------------------------------------------------------------------- /weixin/pages/detail/detail.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ created }} 5 | {{ address }} 6 | 7 | 8 | 9 | {{ think }} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /weixin/pages/detail/detail.wxss: -------------------------------------------------------------------------------- 1 | .section-detail{ 2 | background-color: white; 3 | margin: 20px 20px 0px 20px; 4 | padding: 30px 15px 10px 15px; 5 | border-radius:10px; 6 | 7 | -webkit-box-shadow:2px 2px 5px #BFBFBF; 8 | box-shadow:2px 2px 5px #BFBFBF; 9 | } 10 | 11 | .top { 12 | border-bottom: 1px solid #eee; 13 | } 14 | 15 | .top .detail { 16 | font-size: 13px; 17 | margin-bottom: 40rpx; 18 | padding-right: 10px; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: flex-end; 22 | } 23 | 24 | .content { 25 | font-size: 16px; 26 | margin-top: 40rpx; 27 | overflow-x: scroll; 28 | } 29 | 30 | ::-webkit-scrollbar { 31 | width: 0px; 32 | height: 0px; 33 | color: transparent; 34 | } 35 | 36 | .detail-footer { 37 | height: 50px; 38 | margin-right: 25px; 39 | display: flex; 40 | align-items: center; 41 | float: right; 42 | } 43 | 44 | .detail-footer image { 45 | width: 60rpx; 46 | height: 60rpx; 47 | } 48 | 49 | .detail-footer .edit { 50 | margin-right: 10px; 51 | } -------------------------------------------------------------------------------- /weixin/pages/detail/share/share.js: -------------------------------------------------------------------------------- 1 | import util from '../../../utils/util' 2 | 3 | const app = getApp(); 4 | 5 | Page({ 6 | data: { 7 | windowWidth: 0, 8 | windowHeight: 0, 9 | contentHeight: 0, 10 | thinkList: [], 11 | footer: '', 12 | offset: 0, 13 | lineHeight: 30 14 | }, 15 | 16 | onLoad: function (options) { 17 | let that = this; 18 | that.setData({options: options}); 19 | wx.getSystemInfo({ 20 | success: function (res) { 21 | that.setData({ 22 | windowWidth: res.windowWidth, 23 | windowHeight: res.windowHeight, 24 | offset: (res.windowWidth - 304) / 2 25 | }); 26 | } 27 | }); 28 | }, 29 | 30 | onShow: function () { 31 | app.helper.waitUserSid(this.getApiData) 32 | }, 33 | 34 | getApiData: function () { 35 | let that = this; 36 | 37 | wx.showLoading({ title: '加载中...' }); 38 | app.helper.getApi(app.config.api.think, that.data.options).then(function (res) { 39 | that.setData({footer: res.data.results[0].created_display + ' ' + res.data.results[0].city}); 40 | 41 | let i = 0; 42 | let lineNum = 1; 43 | let thinkStr = ''; 44 | let thinkList = []; 45 | for (let item of res.data.results[0].think) { 46 | if (item === '\n') { 47 | thinkList.push(thinkStr); 48 | thinkList.push('a'); 49 | i = 0; 50 | thinkStr = ''; 51 | lineNum += 1; 52 | } else if (i === 19) { 53 | thinkList.push(thinkStr); 54 | i = 1; 55 | thinkStr = item; 56 | lineNum += 1; 57 | } else { 58 | thinkStr += item; 59 | i += 1; 60 | } 61 | } 62 | thinkList.push(thinkStr); 63 | that.setData({thinkList: thinkList}); 64 | that.createNewImg(lineNum); 65 | }).then(function () { 66 | wx.hideLoading() 67 | }); 68 | }, 69 | 70 | drawSquare: function (ctx, height) { 71 | ctx.setFontSize(24); 72 | ctx.rect(0, 50, this.data.windowWidth, height); 73 | ctx.setFillStyle("#f5f6fd"); 74 | ctx.fill() 75 | }, 76 | 77 | drawFont: function (ctx, content, height) { 78 | ctx.setFontSize(16); 79 | ctx.setFillStyle("#484a3d"); 80 | ctx.fillText(content, this.data.offset, height); 81 | }, 82 | 83 | drawLine: function (ctx, height) { 84 | ctx.beginPath(); 85 | ctx.moveTo(this.data.offset, height); 86 | ctx.lineTo(this.data.windowWidth - this.data.offset, height); 87 | ctx.stroke('#eee'); 88 | ctx.closePath(); 89 | }, 90 | 91 | createNewImg: function (lineNum) { 92 | let that = this; 93 | let ctx = wx.createCanvasContext('myCanvas'); 94 | let contentHeight = lineNum * that.data.lineHeight + 180; 95 | that.drawSquare(ctx, contentHeight); 96 | that.setData({contentHeight: contentHeight}); 97 | let height = 100; 98 | for (let item of that.data.thinkList) { 99 | if (item !== 'a') { 100 | that.drawFont(ctx, item, height); 101 | height += that.data.lineHeight; 102 | } 103 | } 104 | that.drawLine(ctx, lineNum * that.data.lineHeight + 120); 105 | that.drawFont(ctx, that.data.footer, lineNum * that.data.lineHeight + 156); 106 | ctx.drawImage('../../../static/image/think.png', that.data.windowWidth - that.data.offset - 50, lineNum * that.data.lineHeight + 125, 50, 50) 107 | ctx.draw(); 108 | }, 109 | 110 | savePic: function () { 111 | let that = this; 112 | wx.canvasToTempFilePath({ 113 | x: 0, 114 | y: 50, 115 | width: that.data.windowWidth, 116 | height: that.data.contentHeight, 117 | canvasId: 'myCanvas', 118 | success: function (res) { 119 | util.savePicToAlbum(res.tempFilePath) 120 | } 121 | }); 122 | } 123 | }); -------------------------------------------------------------------------------- /weixin/pages/detail/share/share.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "想法" 3 | } -------------------------------------------------------------------------------- /weixin/pages/detail/share/share.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /weixin/pages/detail/share/share.wxss: -------------------------------------------------------------------------------- 1 | .edit-footer { 2 | height: 60px; 3 | margin-top: 20px; 4 | padding-left: 40rpx; 5 | padding-right: 40rpx; 6 | background-color: back; 7 | display: flex; 8 | align-items: center; 9 | } 10 | 11 | .button-done { 12 | width: 100%; 13 | border-radius: 50rpx; 14 | /*background: linear-gradient(to right, red , blue);*/ 15 | } -------------------------------------------------------------------------------- /weixin/pages/edit/edit.js: -------------------------------------------------------------------------------- 1 | var WxNotificationCenter = require('../../utils/WxNotificationCenter.js'); 2 | const app = getApp(); 3 | 4 | Page({ 5 | data: { 6 | windowWidth: 0, 7 | windowHeight: 0, 8 | address: '地点', 9 | nation: '', 10 | province: '', 11 | city: '', 12 | think: '', 13 | options: { 14 | id: '' 15 | } 16 | }, 17 | 18 | onLoad: function (options) { 19 | let that = this; 20 | that.setData({ options: options }); 21 | if (that.data.options.id) { 22 | app.helper.waitUserSid(that.getApiData) 23 | } 24 | 25 | wx.getSystemInfo({ 26 | success: function (res) { 27 | that.setData({ 28 | windowWidth: res.windowWidth, 29 | windowHeight: res.windowHeight 30 | }); 31 | } 32 | }); 33 | 34 | // 注册通知 35 | WxNotificationCenter.addNotification("addressSelectedNotification", that.getAddress, that); 36 | WxNotificationCenter.addNotification("nationSelectedNotification", that.getNation, that); 37 | WxNotificationCenter.addNotification("provinceSelectedNotification", that.getProvince, that); 38 | WxNotificationCenter.addNotification("citySelectedNotification", that.getCity, that); 39 | }, 40 | 41 | onShow: function () { 42 | // let that = this; 43 | }, 44 | 45 | getApiData: function () { 46 | let that = this; 47 | // wx.showLoading({ title: '加载中...' }); 48 | app.helper.getApi(app.config.api.think, that.data.options).then(function (res) { 49 | that.setData({ id: res.data.results[0].id, think: res.data.results[0].think, address: res.data.results[0].address, created: res.data.results[0].created_display }); 50 | if (res.data.results[0].address === '') { 51 | that.setData({address: '地点'}) 52 | } 53 | }).then(function () { 54 | wx.hideLoading() 55 | }); 56 | }, 57 | 58 | keywordTyping: function (e) { 59 | this.setData({ 60 | think: e.detail.value 61 | }); 62 | }, 63 | 64 | getAddress: function (address) { 65 | this.setData({ 66 | address: address 67 | }); 68 | }, 69 | 70 | getNation: function (nation) { 71 | this.setData({ 72 | nation: nation 73 | }); 74 | }, 75 | 76 | getProvince: function (province) { 77 | this.setData({ 78 | province: province 79 | }); 80 | }, 81 | 82 | getCity: function (city) { 83 | this.setData({ 84 | city: city 85 | }); 86 | }, 87 | 88 | thinkDone: function () { 89 | let that = this; 90 | 91 | if (that.data.think !== '') { 92 | if (that.data.address === '地点') { 93 | that.setData({address: ''}) 94 | } 95 | 96 | if (that.data.options.id) { 97 | app.helper.putApi(app.config.api.think, that.data, that.data.options.id + '/').then(function (res) { 98 | let pages = getCurrentPages(); 99 | let backPage = pages[pages.length - 3]; 100 | let results = []; 101 | for (let item of backPage.data.apiData.results) { 102 | if (item['id'] === parseInt(that.data.options.id)) { 103 | item['think'] = that.data.think; 104 | item['nation'] = that.data.nation; 105 | item['province'] = that.data.province; 106 | item['city'] = that.data.city; 107 | item['address'] = that.data.address; 108 | } 109 | results.push(item) 110 | } 111 | backPage.setData({ 'apiData.results': results }) 112 | wx.navigateBack(); 113 | }); 114 | } else { 115 | app.helper.postApi(app.config.api.think, that.data).then(function (res) { 116 | let pages = getCurrentPages(); 117 | let backPage = pages[pages.length - 2]; 118 | app.helper.getApi(app.config.api.think).then(function (res) { 119 | backPage.setData({ apiData: res.data }); 120 | }); 121 | wx.navigateBack(); 122 | }); 123 | } 124 | } else { 125 | wx.showToast({title: '嘿,写想法!', icon: 'fail', duration: 1000}) 126 | } 127 | }, 128 | 129 | selectLocation: function () { 130 | wx.navigateTo({ url: './location/location' }) 131 | } 132 | }); -------------------------------------------------------------------------------- /weixin/pages/edit/edit.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "写想法" 3 | } 4 | -------------------------------------------------------------------------------- /weixin/pages/edit/edit.wxml: -------------------------------------------------------------------------------- 1 | 2 |