Repository: nanbouking/WeTravel
Branch: master
Commit: 3d07a91e98bc
Files: 509
Total size: 1.3 MB
Directory structure:
gitextract_4tbgnvos/
├── .gitignore
├── README.md
├── cloudfunctions/
│ └── mcloud/
│ ├── config/
│ │ └── config.js
│ ├── config.json
│ ├── framework/
│ │ ├── cloud/
│ │ │ ├── cloud_base.js
│ │ │ └── cloud_util.js
│ │ ├── core/
│ │ │ ├── app_code.js
│ │ │ ├── app_error.js
│ │ │ ├── app_other.js
│ │ │ ├── app_util.js
│ │ │ └── application.js
│ │ ├── database/
│ │ │ ├── db_util.js
│ │ │ ├── model.js
│ │ │ └── multi_model.js
│ │ ├── lib/
│ │ │ ├── faker_lib.js
│ │ │ ├── md5_lib.js
│ │ │ └── mini_lib.js
│ │ ├── platform/
│ │ │ ├── controller/
│ │ │ │ ├── base_admin_controller.js
│ │ │ │ └── base_controller.js
│ │ │ ├── model/
│ │ │ │ ├── admin_model.js
│ │ │ │ ├── base_model.js
│ │ │ │ └── log_model.js
│ │ │ └── service/
│ │ │ ├── base_admin_service.js
│ │ │ └── base_service.js
│ │ ├── utils/
│ │ │ ├── constant.js
│ │ │ ├── data_util.js
│ │ │ ├── export_util.js
│ │ │ ├── log_util.js
│ │ │ ├── math_util.js
│ │ │ ├── setup/
│ │ │ │ ├── setup_model.js
│ │ │ │ └── setup_util.js
│ │ │ ├── time_util.js
│ │ │ └── util.js
│ │ └── validate/
│ │ ├── content_check.js
│ │ └── data_check.js
│ ├── index.js
│ ├── package.json
│ └── project/
│ └── TRIP1/
│ ├── controller/
│ │ ├── admin/
│ │ │ ├── admin_album_controller.js
│ │ │ ├── admin_home_controller.js
│ │ │ ├── admin_meet_controller.js
│ │ │ ├── admin_mgr_controller.js
│ │ │ ├── admin_news_controller.js
│ │ │ ├── admin_product_controller.js
│ │ │ ├── admin_setup_controller.js
│ │ │ ├── admin_user_controller.js
│ │ │ └── base_project_admin_controller.js
│ │ ├── album_controller.js
│ │ ├── base_project_controller.js
│ │ ├── check_controller.js
│ │ ├── fav_controller.js
│ │ ├── home_controller.js
│ │ ├── meet_controller.js
│ │ ├── my_controller.js
│ │ ├── news_controller.js
│ │ ├── passport_controller.js
│ │ ├── product_controller.js
│ │ └── test/
│ │ └── test_controller.js
│ ├── model/
│ │ ├── album_model.js
│ │ ├── base_project_model.js
│ │ ├── day_model.js
│ │ ├── fav_model.js
│ │ ├── join_model.js
│ │ ├── meet_model.js
│ │ ├── news_model.js
│ │ ├── product_model.js
│ │ └── user_model.js
│ ├── public/
│ │ ├── constants.js
│ │ ├── project_config.js
│ │ └── route.js
│ └── service/
│ ├── admin/
│ │ ├── admin_album_service.js
│ │ ├── admin_home_service.js
│ │ ├── admin_meet_service.js
│ │ ├── admin_mgr_service.js
│ │ ├── admin_news_service.js
│ │ ├── admin_product_service.js
│ │ ├── admin_setup_service.js
│ │ ├── admin_user_service.js
│ │ └── base_project_admin_service.js
│ ├── album_service.js
│ ├── base_project_service.js
│ ├── fav_service.js
│ ├── home_service.js
│ ├── meet_service.js
│ ├── news_service.js
│ ├── passport_service.js
│ └── product_service.js
├── miniprogram/
│ ├── app.js
│ ├── app.json
│ ├── app.wxss
│ ├── cmpts/
│ │ ├── biz/
│ │ │ ├── detail/
│ │ │ │ ├── detail_cmpt.js
│ │ │ │ ├── detail_cmpt.json
│ │ │ │ ├── detail_cmpt.wxml
│ │ │ │ └── detail_cmpt.wxss
│ │ │ └── foot/
│ │ │ ├── foot_cmpt.js
│ │ │ ├── foot_cmpt.json
│ │ │ ├── foot_cmpt.wxml
│ │ │ └── foot_cmpt.wxss
│ │ └── public/
│ │ ├── calendar/
│ │ │ ├── calendar_comm/
│ │ │ │ ├── calendar_comm_cmpt.js
│ │ │ │ ├── calendar_comm_cmpt.json
│ │ │ │ ├── calendar_comm_cmpt.wxml
│ │ │ │ ├── calendar_comm_cmpt.wxss
│ │ │ │ └── din.wxss
│ │ │ ├── calendar_lib.js
│ │ │ ├── calendar_meet/
│ │ │ │ ├── calendar_meet_cmpt.js
│ │ │ │ ├── calendar_meet_cmpt.json
│ │ │ │ ├── calendar_meet_cmpt.wxml
│ │ │ │ └── calendar_meet_cmpt.wxss
│ │ │ ├── date_select/
│ │ │ │ ├── date_select_cmpt.js
│ │ │ │ ├── date_select_cmpt.json
│ │ │ │ ├── date_select_cmpt.wxml
│ │ │ │ └── date_select_cmpt.wxss
│ │ │ └── time_select/
│ │ │ ├── time_select_cmpt.js
│ │ │ ├── time_select_cmpt.json
│ │ │ ├── time_select_cmpt.wxml
│ │ │ └── time_select_cmpt.wxss
│ │ ├── checkbox/
│ │ │ ├── checkbox_cmpt.js
│ │ │ ├── checkbox_cmpt.json
│ │ │ ├── checkbox_cmpt.wxml
│ │ │ └── checkbox_cmpt.wxss
│ │ ├── editor/
│ │ │ ├── editor_cmpt.js
│ │ │ ├── editor_cmpt.json
│ │ │ ├── editor_cmpt.wxml
│ │ │ └── editor_cmpt.wxss
│ │ ├── form/
│ │ │ ├── form_set/
│ │ │ │ ├── field/
│ │ │ │ │ ├── form_set_field.js
│ │ │ │ │ ├── form_set_field.json
│ │ │ │ │ ├── form_set_field.wxml
│ │ │ │ │ └── form_set_field.wxss
│ │ │ │ ├── form_set_cmpt.js
│ │ │ │ ├── form_set_cmpt.json
│ │ │ │ ├── form_set_cmpt.wxml
│ │ │ │ └── form_set_cmpt.wxss
│ │ │ ├── form_set_helper.js
│ │ │ └── form_show/
│ │ │ ├── content/
│ │ │ │ ├── form_show_content.js
│ │ │ │ ├── form_show_content.json
│ │ │ │ ├── form_show_content.wxml
│ │ │ │ └── form_show_content.wxss
│ │ │ ├── form_show_cmpt.js
│ │ │ ├── form_show_cmpt.json
│ │ │ ├── form_show_cmpt.wxml
│ │ │ └── form_show_cmpt.wxss
│ │ ├── img/
│ │ │ ├── img_upload_cmpt.js
│ │ │ ├── img_upload_cmpt.json
│ │ │ ├── img_upload_cmpt.wxml
│ │ │ └── img_upload_cmpt.wxss
│ │ ├── list/
│ │ │ ├── comm_list_cmpt.js
│ │ │ ├── comm_list_cmpt.json
│ │ │ ├── comm_list_cmpt.wxml
│ │ │ └── comm_list_cmpt.wxss
│ │ ├── modal/
│ │ │ ├── modal_cmpt.js
│ │ │ ├── modal_cmpt.json
│ │ │ ├── modal_cmpt.wxml
│ │ │ └── modal_cmpt.wxss
│ │ ├── picker/
│ │ │ ├── picker_cmpt.js
│ │ │ ├── picker_cmpt.json
│ │ │ ├── picker_cmpt.wxml
│ │ │ └── picker_cmpt.wxss
│ │ ├── picker_multi/
│ │ │ ├── picker_multi_cmpt.js
│ │ │ ├── picker_multi_cmpt.json
│ │ │ ├── picker_multi_cmpt.wxml
│ │ │ └── picker_multi_cmpt.wxss
│ │ ├── picker_time/
│ │ │ ├── datetime_picker.js
│ │ │ ├── picker_time_cmpt.js
│ │ │ ├── picker_time_cmpt.json
│ │ │ ├── picker_time_cmpt.wxml
│ │ │ └── picker_time_cmpt.wxss
│ │ ├── poster/
│ │ │ ├── poster_cmpt.js
│ │ │ ├── poster_cmpt.json
│ │ │ ├── poster_cmpt.wxml
│ │ │ ├── poster_cmpt.wxss
│ │ │ ├── poster_cmpt_helper.js
│ │ │ └── wxa-plugin-canvas/
│ │ │ ├── index/
│ │ │ │ ├── index.js
│ │ │ │ ├── index.json
│ │ │ │ ├── index.wxml
│ │ │ │ └── index.wxss
│ │ │ └── poster/
│ │ │ ├── index.js
│ │ │ ├── index.json
│ │ │ ├── index.wxml
│ │ │ ├── index.wxss
│ │ │ └── poster.js
│ │ ├── radio/
│ │ │ ├── radio_cmpt.js
│ │ │ ├── radio_cmpt.json
│ │ │ ├── radio_cmpt.wxml
│ │ │ └── radio_cmpt.wxss
│ │ ├── swiper/
│ │ │ ├── swiper_cmpt.js
│ │ │ ├── swiper_cmpt.json
│ │ │ ├── swiper_cmpt.wxml
│ │ │ └── swiper_cmpt.wxss
│ │ └── table/
│ │ ├── table_cmpt.js
│ │ ├── table_cmpt.json
│ │ ├── table_cmpt.wxml
│ │ └── table_cmpt.wxss
│ ├── comm/
│ │ ├── behavior/
│ │ │ ├── about_bh.js
│ │ │ ├── my_fav_bh.js
│ │ │ ├── my_foot_bh.js
│ │ │ ├── news_index_bh.js
│ │ │ └── search_bh.js
│ │ ├── biz/
│ │ │ ├── admin_biz.js
│ │ │ ├── base_biz.js
│ │ │ ├── fav_biz.js
│ │ │ ├── foot_biz.js
│ │ │ ├── passport_biz.js
│ │ │ ├── public_biz.js
│ │ │ └── search_biz.js
│ │ └── constants.js
│ ├── helper/
│ │ ├── cache_helper.js
│ │ ├── cloud_helper.js
│ │ ├── content_check_helper.js
│ │ ├── data_helper.js
│ │ ├── file_helper.js
│ │ ├── form_helper.js
│ │ ├── helper.js
│ │ ├── mini_helper.js
│ │ ├── page_helper.js
│ │ ├── pic_helper.js
│ │ ├── time_helper.js
│ │ └── validate.js
│ ├── lib/
│ │ └── tools/
│ │ ├── base64_lib.js
│ │ ├── lunar_lib.js
│ │ └── qrcode_lib.js
│ ├── pages/
│ │ └── test1/
│ │ ├── test1.js
│ │ └── test1.wxml
│ ├── projects/
│ │ └── TRIP1/
│ │ ├── biz/
│ │ │ ├── admin_album_biz.js
│ │ │ ├── admin_meet_biz.js
│ │ │ ├── admin_news_biz.js
│ │ │ ├── admin_product_biz.js
│ │ │ ├── album_biz.js
│ │ │ ├── meet_biz.js
│ │ │ ├── news_biz.js
│ │ │ ├── product_biz.js
│ │ │ └── project_biz.js
│ │ ├── pages/
│ │ │ ├── about/
│ │ │ │ ├── index/
│ │ │ │ │ ├── about_index.js
│ │ │ │ │ ├── about_index.json
│ │ │ │ │ ├── about_index.wxml
│ │ │ │ │ └── about_index.wxss
│ │ │ │ └── service/
│ │ │ │ ├── about_service.js
│ │ │ │ ├── about_service.json
│ │ │ │ ├── about_service.wxml
│ │ │ │ └── about_service.wxss
│ │ │ ├── admin/
│ │ │ │ ├── album/
│ │ │ │ │ ├── add/
│ │ │ │ │ │ ├── admin_album_add.js
│ │ │ │ │ │ ├── admin_album_add.json
│ │ │ │ │ │ ├── admin_album_add.wxml
│ │ │ │ │ │ └── admin_album_add.wxss
│ │ │ │ │ ├── admin_album_form_tpl.wxml
│ │ │ │ │ ├── edit/
│ │ │ │ │ │ ├── admin_album_edit.js
│ │ │ │ │ │ ├── admin_album_edit.json
│ │ │ │ │ │ ├── admin_album_edit.wxml
│ │ │ │ │ │ └── admin_album_edit.wxss
│ │ │ │ │ └── list/
│ │ │ │ │ ├── admin_album_list.js
│ │ │ │ │ ├── admin_album_list.json
│ │ │ │ │ ├── admin_album_list.wxml
│ │ │ │ │ └── admin_album_list.wxss
│ │ │ │ ├── content/
│ │ │ │ │ ├── admin_content.js
│ │ │ │ │ ├── admin_content.json
│ │ │ │ │ ├── admin_content.wxml
│ │ │ │ │ └── admin_content.wxss
│ │ │ │ ├── index/
│ │ │ │ │ ├── home/
│ │ │ │ │ │ ├── admin_home.js
│ │ │ │ │ │ ├── admin_home.json
│ │ │ │ │ │ ├── admin_home.wxml
│ │ │ │ │ │ └── admin_home.wxss
│ │ │ │ │ └── login/
│ │ │ │ │ ├── admin_login.js
│ │ │ │ │ ├── admin_login.json
│ │ │ │ │ ├── admin_login.wxml
│ │ │ │ │ └── admin_login.wxss
│ │ │ │ ├── meet/
│ │ │ │ │ ├── cover/
│ │ │ │ │ │ ├── admin_meet_cover.js
│ │ │ │ │ │ ├── admin_meet_cover.json
│ │ │ │ │ │ ├── admin_meet_cover.wxml
│ │ │ │ │ │ └── admin_meet_cover.wxss
│ │ │ │ │ ├── edit/
│ │ │ │ │ │ ├── admin_meet_edit.js
│ │ │ │ │ │ ├── admin_meet_edit.json
│ │ │ │ │ │ ├── admin_meet_edit.wxml
│ │ │ │ │ │ └── admin_meet_edit.wxss
│ │ │ │ │ ├── export/
│ │ │ │ │ │ ├── admin_join_export.js
│ │ │ │ │ │ ├── admin_join_export.json
│ │ │ │ │ │ ├── admin_join_export.wxml
│ │ │ │ │ │ └── admin_join_export.wxss
│ │ │ │ │ ├── join/
│ │ │ │ │ │ ├── admin_meet_join.js
│ │ │ │ │ │ ├── admin_meet_join.json
│ │ │ │ │ │ ├── admin_meet_join.wxml
│ │ │ │ │ │ └── admin_meet_join.wxss
│ │ │ │ │ ├── list/
│ │ │ │ │ │ ├── admin_meet_list.js
│ │ │ │ │ │ ├── admin_meet_list.json
│ │ │ │ │ │ ├── admin_meet_list.wxml
│ │ │ │ │ │ └── admin_meet_list.wxss
│ │ │ │ │ ├── record/
│ │ │ │ │ │ ├── admin_record_list.js
│ │ │ │ │ │ ├── admin_record_list.json
│ │ │ │ │ │ ├── admin_record_list.wxml
│ │ │ │ │ │ └── admin_record_list.wxss
│ │ │ │ │ ├── scan/
│ │ │ │ │ │ ├── admin_meet_scan.js
│ │ │ │ │ │ ├── admin_meet_scan.json
│ │ │ │ │ │ ├── admin_meet_scan.wxml
│ │ │ │ │ │ └── admin_meet_scan.wxss
│ │ │ │ │ ├── self/
│ │ │ │ │ │ ├── admin_meet_self.js
│ │ │ │ │ │ ├── admin_meet_self.json
│ │ │ │ │ │ ├── admin_meet_self.wxml
│ │ │ │ │ │ └── admin_meet_self.wxss
│ │ │ │ │ ├── temp/
│ │ │ │ │ │ ├── admin_temp_select.js
│ │ │ │ │ │ ├── admin_temp_select.json
│ │ │ │ │ │ ├── admin_temp_select.wxml
│ │ │ │ │ │ └── admin_temp_select.wxss
│ │ │ │ │ └── time/
│ │ │ │ │ ├── admin_meet_time.js
│ │ │ │ │ ├── admin_meet_time.json
│ │ │ │ │ ├── admin_meet_time.wxml
│ │ │ │ │ └── admin_meet_time.wxss
│ │ │ │ ├── mgr/
│ │ │ │ │ ├── add/
│ │ │ │ │ │ ├── admin_mgr_add.js
│ │ │ │ │ │ ├── admin_mgr_add.json
│ │ │ │ │ │ ├── admin_mgr_add.wxml
│ │ │ │ │ │ └── admin_mgr_add.wxss
│ │ │ │ │ ├── edit/
│ │ │ │ │ │ ├── admin_mgr_edit.js
│ │ │ │ │ │ ├── admin_mgr_edit.json
│ │ │ │ │ │ ├── admin_mgr_edit.wxml
│ │ │ │ │ │ └── admin_mgr_edit.wxss
│ │ │ │ │ ├── list/
│ │ │ │ │ │ ├── admin_mgr_list.js
│ │ │ │ │ │ ├── admin_mgr_list.json
│ │ │ │ │ │ ├── admin_mgr_list.wxml
│ │ │ │ │ │ └── admin_mgr_list.wxss
│ │ │ │ │ ├── log/
│ │ │ │ │ │ ├── admin_log_list.js
│ │ │ │ │ │ ├── admin_log_list.json
│ │ │ │ │ │ ├── admin_log_list.wxml
│ │ │ │ │ │ └── admin_log_list.wxss
│ │ │ │ │ └── pwd/
│ │ │ │ │ ├── admin_mgr_pwd.js
│ │ │ │ │ ├── admin_mgr_pwd.json
│ │ │ │ │ ├── admin_mgr_pwd.wxml
│ │ │ │ │ └── admin_mgr_pwd.wxss
│ │ │ │ ├── news/
│ │ │ │ │ ├── add/
│ │ │ │ │ │ ├── admin_news_add.js
│ │ │ │ │ │ ├── admin_news_add.json
│ │ │ │ │ │ ├── admin_news_add.wxml
│ │ │ │ │ │ └── admin_news_add.wxss
│ │ │ │ │ ├── admin_news_form_tpl.wxml
│ │ │ │ │ ├── edit/
│ │ │ │ │ │ ├── admin_news_edit.js
│ │ │ │ │ │ ├── admin_news_edit.json
│ │ │ │ │ │ ├── admin_news_edit.wxml
│ │ │ │ │ │ └── admin_news_edit.wxss
│ │ │ │ │ └── list/
│ │ │ │ │ ├── admin_news_list.js
│ │ │ │ │ ├── admin_news_list.json
│ │ │ │ │ ├── admin_news_list.wxml
│ │ │ │ │ └── admin_news_list.wxss
│ │ │ │ ├── product/
│ │ │ │ │ ├── add/
│ │ │ │ │ │ ├── admin_product_add.js
│ │ │ │ │ │ ├── admin_product_add.json
│ │ │ │ │ │ ├── admin_product_add.wxml
│ │ │ │ │ │ └── admin_product_add.wxss
│ │ │ │ │ ├── admin_product_form_tpl.wxml
│ │ │ │ │ ├── edit/
│ │ │ │ │ │ ├── admin_product_edit.js
│ │ │ │ │ │ ├── admin_product_edit.json
│ │ │ │ │ │ ├── admin_product_edit.wxml
│ │ │ │ │ │ └── admin_product_edit.wxss
│ │ │ │ │ └── list/
│ │ │ │ │ ├── admin_product_list.js
│ │ │ │ │ ├── admin_product_list.json
│ │ │ │ │ ├── admin_product_list.wxml
│ │ │ │ │ └── admin_product_list.wxss
│ │ │ │ ├── setup/
│ │ │ │ │ ├── about/
│ │ │ │ │ │ ├── admin_setup_about.js
│ │ │ │ │ │ ├── admin_setup_about.json
│ │ │ │ │ │ ├── admin_setup_about.wxml
│ │ │ │ │ │ └── admin_setup_about.wxss
│ │ │ │ │ ├── about_list/
│ │ │ │ │ │ ├── admin_setup_about_list.js
│ │ │ │ │ │ ├── admin_setup_about_list.json
│ │ │ │ │ │ ├── admin_setup_about_list.wxml
│ │ │ │ │ │ └── admin_setup_about_list.wxss
│ │ │ │ │ └── qr/
│ │ │ │ │ ├── admin_setup_qr.js
│ │ │ │ │ ├── admin_setup_qr.json
│ │ │ │ │ ├── admin_setup_qr.wxml
│ │ │ │ │ └── admin_setup_qr.wxss
│ │ │ │ └── user/
│ │ │ │ ├── detail/
│ │ │ │ │ ├── admin_user_detail.js
│ │ │ │ │ ├── admin_user_detail.json
│ │ │ │ │ ├── admin_user_detail.wxml
│ │ │ │ │ └── admin_user_detail.wxss
│ │ │ │ ├── export/
│ │ │ │ │ ├── admin_user_export.js
│ │ │ │ │ ├── admin_user_export.json
│ │ │ │ │ ├── admin_user_export.wxml
│ │ │ │ │ └── admin_user_export.wxss
│ │ │ │ └── list/
│ │ │ │ ├── admin_user_list.js
│ │ │ │ ├── admin_user_list.json
│ │ │ │ ├── admin_user_list.wxml
│ │ │ │ └── admin_user_list.wxss
│ │ │ ├── album/
│ │ │ │ ├── detail/
│ │ │ │ │ ├── album_detail.js
│ │ │ │ │ ├── album_detail.json
│ │ │ │ │ ├── album_detail.wxml
│ │ │ │ │ └── album_detail.wxss
│ │ │ │ └── index/
│ │ │ │ ├── album_index.js
│ │ │ │ ├── album_index.json
│ │ │ │ ├── album_index.wxml
│ │ │ │ └── album_index.wxss
│ │ │ ├── default/
│ │ │ │ └── index/
│ │ │ │ ├── default_index.js
│ │ │ │ ├── default_index.json
│ │ │ │ ├── default_index.wxml
│ │ │ │ └── default_index.wxss
│ │ │ ├── meet/
│ │ │ │ ├── calendar/
│ │ │ │ │ ├── meet_calendar.js
│ │ │ │ │ ├── meet_calendar.json
│ │ │ │ │ ├── meet_calendar.wxml
│ │ │ │ │ └── meet_calendar.wxss
│ │ │ │ ├── detail/
│ │ │ │ │ ├── meet_detail.js
│ │ │ │ │ ├── meet_detail.json
│ │ │ │ │ ├── meet_detail.wxml
│ │ │ │ │ └── meet_detail.wxss
│ │ │ │ ├── index/
│ │ │ │ │ ├── meet_index.js
│ │ │ │ │ ├── meet_index.json
│ │ │ │ │ ├── meet_index.wxml
│ │ │ │ │ └── meet_index.wxss
│ │ │ │ ├── join/
│ │ │ │ │ ├── meet_join.js
│ │ │ │ │ ├── meet_join.json
│ │ │ │ │ ├── meet_join.wxml
│ │ │ │ │ └── meet_join.wxss
│ │ │ │ ├── my_join_detail/
│ │ │ │ │ ├── meet_my_join_detail.js
│ │ │ │ │ ├── meet_my_join_detail.json
│ │ │ │ │ ├── meet_my_join_detail.wxml
│ │ │ │ │ └── meet_my_join_detail.wxss
│ │ │ │ ├── my_join_list/
│ │ │ │ │ ├── meet_my_join_list.js
│ │ │ │ │ ├── meet_my_join_list.json
│ │ │ │ │ ├── meet_my_join_list.wxml
│ │ │ │ │ └── meet_my_join_list.wxss
│ │ │ │ └── self/
│ │ │ │ ├── meet_self.js
│ │ │ │ ├── meet_self.json
│ │ │ │ ├── meet_self.wxml
│ │ │ │ └── meet_self.wxss
│ │ │ ├── my/
│ │ │ │ ├── edit/
│ │ │ │ │ ├── my_edit.js
│ │ │ │ │ ├── my_edit.json
│ │ │ │ │ ├── my_edit.wxml
│ │ │ │ │ ├── my_edit.wxss
│ │ │ │ │ └── user_form.wxml
│ │ │ │ ├── fav/
│ │ │ │ │ ├── my_fav.js
│ │ │ │ │ ├── my_fav.json
│ │ │ │ │ ├── my_fav.wxml
│ │ │ │ │ └── my_fav.wxss
│ │ │ │ ├── foot/
│ │ │ │ │ ├── my_foot.js
│ │ │ │ │ ├── my_foot.json
│ │ │ │ │ ├── my_foot.wxml
│ │ │ │ │ └── my_foot.wxss
│ │ │ │ ├── index/
│ │ │ │ │ ├── my_index.js
│ │ │ │ │ ├── my_index.json
│ │ │ │ │ ├── my_index.wxml
│ │ │ │ │ └── my_index.wxss
│ │ │ │ └── reg/
│ │ │ │ ├── my_reg.js
│ │ │ │ ├── my_reg.json
│ │ │ │ ├── my_reg.wxml
│ │ │ │ └── my_reg.wxss
│ │ │ ├── news/
│ │ │ │ ├── cate1/
│ │ │ │ │ ├── news_cate1.js
│ │ │ │ │ ├── news_cate1.json
│ │ │ │ │ ├── news_cate1.wxml
│ │ │ │ │ └── news_cate1.wxss
│ │ │ │ ├── cate2/
│ │ │ │ │ ├── news_cate2.js
│ │ │ │ │ ├── news_cate2.json
│ │ │ │ │ ├── news_cate2.wxml
│ │ │ │ │ └── news_cate2.wxss
│ │ │ │ ├── detail/
│ │ │ │ │ ├── news_detail.js
│ │ │ │ │ ├── news_detail.json
│ │ │ │ │ ├── news_detail.wxml
│ │ │ │ │ └── news_detail.wxss
│ │ │ │ └── index/
│ │ │ │ ├── news_index.js
│ │ │ │ ├── news_index.json
│ │ │ │ ├── news_index.wxml
│ │ │ │ └── news_index.wxss
│ │ │ ├── product/
│ │ │ │ ├── detail/
│ │ │ │ │ ├── product_detail.js
│ │ │ │ │ ├── product_detail.json
│ │ │ │ │ ├── product_detail.wxml
│ │ │ │ │ └── product_detail.wxss
│ │ │ │ └── index/
│ │ │ │ ├── product_index.js
│ │ │ │ ├── product_index.json
│ │ │ │ ├── product_index.wxml
│ │ │ │ └── product_index.wxss
│ │ │ ├── search/
│ │ │ │ ├── search.js
│ │ │ │ ├── search.json
│ │ │ │ ├── search.wxml
│ │ │ │ └── search.wxss
│ │ │ └── tpls/
│ │ │ └── menu_tpl.wxml
│ │ ├── public/
│ │ │ └── project_setting.js
│ │ └── style/
│ │ └── skin.wxss
│ ├── setting/
│ │ └── setting.js
│ ├── sitemap.json
│ ├── style/
│ │ ├── base/
│ │ │ ├── animation.wxss
│ │ │ ├── avatar.wxss
│ │ │ ├── background.wxss
│ │ │ ├── bar.wxss
│ │ │ ├── base.wxss
│ │ │ ├── border.wxss
│ │ │ ├── button.wxss
│ │ │ ├── comm.wxss
│ │ │ ├── form.wxss
│ │ │ ├── icon.wxss
│ │ │ ├── image.wxss
│ │ │ ├── layout.wxss
│ │ │ ├── list.wxss
│ │ │ ├── load.wxss
│ │ │ ├── modal.wxss
│ │ │ ├── nav.wxss
│ │ │ ├── shadow.wxss
│ │ │ ├── table.wxss
│ │ │ ├── tag.wxss
│ │ │ └── text.wxss
│ │ ├── project/
│ │ │ ├── admin_list_style.wxss
│ │ │ ├── my_fav_style.wxss
│ │ │ ├── my_foot_style.wxss
│ │ │ └── search_style.wxss
│ │ └── public/
│ │ ├── admin.wxss
│ │ ├── article_list.wxss
│ │ ├── comm_box_list.wxss
│ │ ├── detail.wxss
│ │ └── project.wxss
│ └── tpls/
│ ├── project/
│ │ ├── about_tpl.wxml
│ │ ├── my_fav_tpl.wxml
│ │ ├── my_foot_tpl.wxml
│ │ ├── news_index_tpl.wxml
│ │ └── search_tpl.wxml
│ ├── public/
│ │ ├── admin_forms_detail_tpl.wxml
│ │ ├── base_list_tpl.wxml
│ │ ├── list_load_tpl.wxml
│ │ └── top_tpl.wxml
│ └── wxs/
│ └── tools.wxs
├── project.config.json
├── project.private.config.json
└── 旅游景区门户小程序安装使用手册.docx
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Windows
[Dd]esktop.ini
Thumbs.db
$RECYCLE.BIN/
# macOS
.DS_Store
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
# Node.js
node_modules/
================================================
FILE: README.md
================================================
## 功能 介绍
旅游景区门户小程序是一个为游客朋友提供多元化服务的一站式数字文旅平台,提供“吃、住、游、购、娱”等旅游行程中的多种服务,全面支持景区介绍,景点攻略,景点预约,停车预约、景区导览等功能,游客可以在小程序中了解旅游度假区的景点信息、历史文化、各类活动等。前后端代码完整,采用腾讯提供的小程序云开发解决方案,无须服务器和域名。
- 景点预约管理:开始/截止时间/人数均可灵活设置,可以自定义客户预约填写的数据项
- 景点预约凭证:支持线下到场后校验签到/核销/二维码自助签到等多种方式
- 详尽的预约数据:支持预约名单数据导出Excel,打印


## 技术运用
- 本项目使用微信小程序平台进行开发。
- 使用腾讯专门的小程序云开发技术,云资源包含云函数,数据库,带宽,存储空间,定时器等,资源配额价格低廉,无需域名和服务器即可搭建。
- 小程序本身的即用即走,适合小工具的使用场景,也适合快速开发迭代。
- 云开发技术采用腾讯内部链路,没有被黑客攻击的风险,安全性高且免维护。
- 资源承载力可根据业务发展需要随时弹性扩展。
## 作者
- 如有疑问,欢迎骚扰联系我:开发交流,技术分享,问题答疑,功能建议收集,版本更新通知,安装部署协助,小程序开发定制等。
- 俺的微信:

## 演示

## 安装
- 安装手册见源码包里的word文档










## 截图
## 后台管理系统截图
- 后台超级管理员默认账号:admin,密码123456,请登录后台后及时修改密码和创建普通管理员。







================================================
FILE: cloudfunctions/mcloud/config/config.js
================================================
module.exports = {
//### 环境相关
CLOUD_ID: 'init-5go8b8pdc98ea814', //你的云环境id
// ##################################################################
COLLECTION_PRFIX: 'bx_',
IS_DEMO: false, //是否演示版 (后台不可操作提交动作)
// ##################################################################
// #### 调试相关
TEST_MODE: false, // 测试模式 涉及小程序码生成路径, 用以下 TEST_TOKEN_ID openid..
TEST_TOKEN_ID: 'oD58U5Ej-gK0BjqSspqjQEPgXuQQ',
// #### 内容安全
CLIENT_CHECK_CONTENT: false, //前台图片文字是否校验
ADMIN_CHECK_CONTENT: false, //后台图片文字是否校验
// ### 后台业务相关
ADMIN_LOGIN_EXPIRE: 86400, //管理员token过期时间 (秒)
}
================================================
FILE: cloudfunctions/mcloud/config.json
================================================
{
"permissions": {
"openapi": ["wxacode.getUnlimited", "security.imgSecCheck", "security.msgSecCheck","serviceMarket.invokeService"]
}
}
================================================
FILE: cloudfunctions/mcloud/framework/cloud/cloud_base.js
================================================
/**
* Notes: 云初始化实例
* Ver : CCMiniCloud Framework 2.2.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const config = require('../../config/config.js');
/**
* 获得云实例
*/
function getCloud() {
const cloud = require('wx-server-sdk');
cloud.init({
env: config.CLOUD_ID
});
return cloud;
}
module.exports = {
getCloud
}
================================================
FILE: cloudfunctions/mcloud/framework/cloud/cloud_util.js
================================================
/**
* Notes: 云基本操作模块
* Ver : CCMiniCloud Framework 2.3.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const cloudBase = require('./cloud_base.js');
function log(method, err, level = 'error') {
const cloud = cloudBase.getCloud();
const log = cloud.logger();
log.error({
method: method,
errCode: err.code,
errMsg: err.message,
errStack: err.stack
});
}
async function getTempFileURLOne(fileID) {
if (!fileID) return '';
const cloud = cloudBase.getCloud();
let result = await cloud.getTempFileURL({
fileList: [fileID],
})
if (result && result.fileList && result.fileList[0] && result.fileList[0].tempFileURL)
return result.fileList[0].tempFileURL;
return '';
}
async function getTempFileURL(tempFileList, isValid = false) {
if (!tempFileList || tempFileList.length == 0) return [];
const cloud = cloudBase.getCloud();
let result = await cloud.getTempFileURL({
fileList: tempFileList,
})
console.log(result);
let list = result.fileList;
let outList = [];
for (let i = 0; i < list.length; i++) {
let pic = {};
if (list[i].status == 0) {
//获取到地址的
pic.url = list[i].tempFileURL;
pic.cloudId = list[i].fileID;
outList.push(pic)
} else {
//未获取到地址的(已经转换过的)
if (!isValid) {
pic.url = list[i].fileID; // fileID为URL, tempFileURL为空
pic.cloudId = list[i].fileID;
outList.push(pic)
}
}
}
return outList;
}
async function handlerCloudFilesForForms(oldForms, newsForms) {
let oldFiles = [];
let newFiles = [];
for (let k = 0; k < oldForms.length; k++) {
if (oldForms[k].type == 'image')
oldFiles = oldFiles.concat(oldForms[k].val);
else if (oldForms[k].type == 'content') {
let contentVal = oldForms[k].val;
for (let n in contentVal) {
if (contentVal[n].type == 'img')
oldFiles.push(contentVal[n].val);
}
}
}
for (let j in newsForms) {
if (newsForms[j].type == 'image')
newFiles = newFiles.concat(newsForms[j].val);
else if (newsForms[j].type == 'content') {
let contentVal = newsForms[j].val;
for (let m in contentVal) {
if (contentVal[m].type == 'img')
newFiles.push(contentVal[m].val);
}
}
}
await handlerCloudFiles(oldFiles, newFiles);
}
async function handlerCloudFiles(oldFiles, newFiles) {
//if (oldFiles.length == 0 && newFiles.length == 0) return [];
const cloud = cloudBase.getCloud();
for (let i = 0; i < oldFiles.length; i++) {
let isDel = true;
for (let j = 0; j < newFiles.length; j++) {
if (oldFiles[i] == newFiles[j]) {
// 从旧文件数组里 找到 新组 还存在的文件 ,则不删除
isDel = false;
break;
}
}
// 新组里不存在,直接删除
if (isDel && oldFiles[i]) {
let result = await cloud.deleteFile({
fileList: [oldFiles[i]],
});
console.log(result);
}
}
return newFiles;
}
async function handlerCloudFilesByRichEditor(oldFiles, newFiles) {
const cloud = cloudBase.getCloud();
for (let i = 0; i < oldFiles.length; i++) {
let isDel = true;
for (let j = 0; j < newFiles.length; j++) {
if (oldFiles[i].type == 'img' && newFiles[j].type == 'img' && oldFiles[i].val == newFiles[j].val) {
// 从旧文件数组里 找到 新组 还存在的图片文件, 保存cloudID
//newFiles[j].cloudId = oldFiles[i].cloudId;
isDel = false;
break;
}
}
// 新组里不存在,直接删除
if (isDel && oldFiles[i].type == 'img' && oldFiles[i].val) {
let result = await cloud.deleteFile({
fileList: [oldFiles[i].val],
});
}
}
return newFiles;
}
async function deleteFiles(list) {
if (!list) return;
if (!Array.isArray(list)) list = [list];
if (list.length == 0) return;
const cloud = cloudBase.getCloud();
await cloud.deleteFile({
fileList: list,
});
}
module.exports = {
log,
getTempFileURL,
getTempFileURLOne,
deleteFiles,
handlerCloudFiles,
handlerCloudFilesByRichEditor,
handlerCloudFilesForForms
}
================================================
FILE: cloudfunctions/mcloud/framework/core/app_code.js
================================================
/**
* Notes: 错误代码定义
* Ver : CCMiniCloud Framework 2.4.1 ALL RIGHTS RESERVED BY Cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
module.exports = {
SUCC: 200,
SVR: 500, // 服务器错误
LOGIC: 1600, //逻辑错误
DATA: 1301, // 数据校验错误
HEADER: 1302, // header 校验错误
//2000开始为业务错误代码,
ADMIN_ERROR: 2401 //管理员错误
}
================================================
FILE: cloudfunctions/mcloud/framework/core/app_error.js
================================================
/**
* Notes: 应用异常处理类
* Ver : CCMiniCloud Framework 2.5.1 ALL RIGHTS RESERVED BY cClinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const appCode = require('./app_code.js');
class AppError extends Error {
constructor(message, code = appCode.LOGIC) {
super(message);
this.name = 'AppError';
this.code = code;
}
}
module.exports = AppError;
================================================
FILE: cloudfunctions/mcloud/framework/core/app_other.js
================================================
/**
* Notes: 云函数非标业务处理
* Ver : CCMiniCloud Framework 2.6.1 ALL RIGHTS RESERVED BY ccLinux@qq.com
* Date: 2021-10-21 04:00:00
*/
function handlerOther(event) {
let isOther = false;
if (!event) return {
isOther,
eventX
};
// 公众号事件处理
if (event['FromUserName'] && event['MsgType']) {
console.log('公众号事件处理');
let ret = {
route: 'oa/serve',
params: event
}
return {
isOther: true,
eventX: ret
};
}
return {
isOther,
eventX: event
};
}
module.exports = {
handlerOther,
}
================================================
FILE: cloudfunctions/mcloud/framework/core/app_util.js
================================================
/**
* Notes: 云函数业务主逻辑函数
* Ver : CCMiniCloud Framework 2.7.1 ALL RIGHTS RESERVED BY cclinuX0730 (wechat)
* Date: 2021-02-09 04:00:00
*/
const appCode = require('./app_code.js');
function handlerBasic(code, msg = '', data = {}) {
switch (code) {
case appCode.SUCC:
msg = (msg) ? msg + ':ok' : 'ok';
break;
case appCode.SVR:
msg = '服务器繁忙,请稍后再试';
break;
case appCode.LOGIC:
break;
case appCode.DATA:
break;
/*
default:
msg = '服务器开小差了,请稍后再试';
break;*/
}
return {
code: code,
msg: msg,
data: data
}
}
function handlerSvrErr(msg = '') {
return handlerBasic(appCode.SVR, msg);
}
function handlerSucc(msg = '') {
return handlerBasic(appCode.SUCC, msg);
}
function handlerAppErr(msg = '', code = appCode.LOGIC) {
return handlerBasic(code, msg);
}
function handlerData(data, msg = '') {
return handlerBasic(appCode.SUCC, msg, data);
}
module.exports = {
handlerBasic,
handlerData,
handlerSucc,
handlerSvrErr,
handlerAppErr
}
================================================
FILE: cloudfunctions/mcloud/framework/core/application.js
================================================
/**
* Notes: 云函数业务主逻辑
* Ver : CCMiniCloud Framework 2.8.1 ALL RIGHTS RESERVED BY cclinUX0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const util = require('../utils/util.js');
const cloudBase = require('../cloud/cloud_base.js');
const timeUtil = require('../utils/time_util.js');
const appUtil = require('./app_util.js');
const appCode = require('./app_code.js');
const appOther = require('./app_other.js');
const config = require('../../config/config.js');
global.PID = 'unknown';
async function app(event, context) {
// 非标业务处理
let {
eventX,
isOther
} = appOther.handlerOther(event);
event = eventX;
// 取得openid
const cloud = cloudBase.getCloud();
const wxContext = cloud.getWXContext();
let r = '';
let PID = '';
try {
if (!util.isDefined(event.route)) {
showEvent(event);
console.error('Route Not Defined');
return appUtil.handlerSvrErr();
}
r = event.route.toLowerCase();
if (!r.includes('/')) {
showEvent(event);
console.error('Route Format error[' + r + ']');
return appUtil.handlerSvrErr();
}
PID = event.PID.trim();
if (!PID) {
showEvent(event);
console.error('PID Is NULL]');
return appUtil.handlerSvrErr();
}
global.PID = PID;
// 路由不存在
routes = require('project/' + PID + '/public/route.js');
if (!util.isDefined(routes[r])) {
showEvent(event);
console.error('Route [' + r + '] Is Not Exist');
return appUtil.handlerSvrErr();
}
let routesArr = routes[r].split('@');
let controllerName = routesArr[0];
let actionName = routesArr[1];
// 事前处理
if (actionName.includes('#')) {
let actionNameArr = actionName.split('#');
actionName = actionNameArr[0];
if (actionNameArr[1] && config.IS_DEMO) {
console.log('### APP Before = ' + actionNameArr[1]);
return beforeApp(actionNameArr[1]);
}
}
console.log('');
console.log('');
let time = timeUtil.time('Y-M-D h:m:s');
let timeTicks = timeUtil.time();
let openId = wxContext.OPENID;
console.log('▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤▤');
console.log(`【↘${time} ENV (${config.CLOUD_ID})】【Request Base↘↘↘】\n【↘Route =***${r}】\n【↘Controller = ${controllerName}】\n【↘Action = ${actionName}】\n【↘OPENID = ${openId}】\n【↘PID = ${global.PID}】`);
// 测试模式
if (config.TEST_MODE)
openId = config.TEST_TOKEN_ID;
if (!openId) {
console.error('OPENID is unfined');
return appUtil.handlerSvrErr();
}
// 引入逻辑controller
controllerName = controllerName.toLowerCase().trim();
const ControllerClass = require('project/'+PID +'/controller/' + controllerName + '.js');
const controller = new ControllerClass(r, PID + '^^^' + openId, event);
// 调用方法
await controller['initSetup']();
let result = await controller[actionName]();
// 返回值处理
if (isOther) {
// 非标处理
return result;
} else {
if (!result)
result = appUtil.handlerSucc(r); // 无数据返回
else
result = appUtil.handlerData(result, r); // 有数据返回
}
console.log('------');
time = timeUtil.time('Y-M-D h:m:s');
timeTicks = timeUtil.time() - timeTicks;
console.log(`【${time}】【Return Base↗↗↗】\n【↗Route =***${r}】\n【↗Duration = ${timeTicks}ms】\n【↗↗OUT DATA】= `, result);
console.log('▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦');
console.log('');
console.log('');
return result;
} catch (ex) {
const log = cloud.logger();
console.log('------');
time = timeUtil.time('Y-M-D h:m:s');
console.error(`【${time}】【Return Base↗↗↗】\n【↗Route = ${r}】\Exception MSG = ${ex.message}, CODE=${ex.code}`);
// 系统级错误定位调试
if (config.TEST_MODE && ex.name != 'AppError') throw ex;
console.log('▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦');
console.log('');
console.log('');
if (ex.name == 'AppError') {
log.warn({
route: r,
errCode: ex.code,
errMsg: ex.message
});
// 自定义error处理
return appUtil.handlerAppErr(ex.message, ex.code);
} else {
//console.log(ex);
log.error({
route: r,
errCode: ex.code,
errMsg: ex.message,
errStack: ex.stack
});
// 系统error
return appUtil.handlerSvrErr();
}
}
}
// 事前处理
function beforeApp(method) {
switch (method) {
case 'demo': {
return appUtil.handlerAppErr('本系统仅为用户体验演示,后台提交的操作均不生效!如有需要请联系作者微信cclinux0730', appCode.LOGIC);
}
}
console.error('事前处理, Method Not Find = ' + method);
}
// 展示当前输入数据
function showEvent(event) {
console.log(event);
}
module.exports = {
app
}
================================================
FILE: cloudfunctions/mcloud/framework/database/db_util.js
================================================
/**
* Notes: 数据库基本操作
* Ver : CCMiniCloud Framework 2.9.1 ALL RIGHTS RESERVED BY CClinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const util = require('../utils/util.js');
const dataUtil = require('../utils/data_util.js');
const cloudBase = require('../cloud/cloud_base.js');
const MAX_RECORD_SIZE = 1000;
const DEFAULT_RECORD_SIZE = 20;
const cloud = cloudBase.getCloud();
const db = cloud.database();
const dbCmd = db.command;
const dbAggr = dbCmd.aggregate;
// 获得一个命令操作
function getCmd() {
return dbCmd;
}
async function insertBatch(collectionName, data, size = 1000) {
let dataArr = dataUtil.spArr(data, size);
for (let k = 0; k < dataArr.length; k++) {
await db.collection(collectionName).add({
data: dataArr[k]
});
}
//return query._id;
}
async function insert(collectionName, data) {
let query = await db.collection(collectionName).add({
data
});
return query._id;
}
async function edit(collectionName, where, data) {
let query = await db.collection(collectionName);
// 查询条件
if (util.isDefined(where)) {
if (typeof (where) == 'string' || typeof (where) == 'number')
query = await query.doc(where);
else
query = await query.where(fmtWhere(where));
}
query = await query.update({
data
});
return query.stats.updated;
}
async function inc(collectionName, where, field, val = 1) {
let query = await db.collection(collectionName);
// 查询条件
if (util.isDefined(where)) {
if (typeof (where) == 'string' || typeof (where) == 'number')
query = await query.doc(where);
else
query = await query.where(fmtWhere(where));
}
query = await query.update({
data: {
[field]: dbCmd.inc(val)
}
});
return query.stats.updated;
}
async function mul(collectionName, where, field, val = 1) {
let query = await db.collection(collectionName);
// 查询条件
if (util.isDefined(where)) {
if (typeof (where) == 'string' || typeof (where) == 'number')
query = await query.doc(where);
else
query = await query.where(fmtWhere(where));
}
query = await query.update({
data: {
[field]: dbCmd.mul(val)
}
});
return query.stats.updated;
}
async function del(collectionName, where) {
let query = await db.collection(collectionName);
// 查询条件
if (util.isDefined(where)) {
if (typeof (where) == 'string' || typeof (where) == 'number')
query = await query.doc(where);
else
query = await query.where(fmtWhere(where));
}
query = await query.remove();
return query.stats.removed;
}
async function count(collectionName, where) {
let query = await db.collection(collectionName);
// 查询条件
if (typeof (where) == 'string' || typeof (where) == 'number')
query = await query.doc(where);
else
query = await query.where(fmtWhere(where));
query = await query.count();
return query.total;
}
async function distinct(collectionName, where, field) {
let query = await db.collection(collectionName);
query = await query.aggregate();
// 查询条件
query = await query.match(fmtWhere(where));
query = await query.group({
_id: null,
'uniqueValues': dbAggr.addToSet('$' + field)
}).end();
if (query && query.list && query.list[0] && query.list[0]['uniqueValues']) {
return query.list[0]['uniqueValues'];
} else
return [];
}
async function distinctCnt(collectionName, where, field) {
let data = await distinct(collectionName, where, field);
return data.length;
}
async function groupSum(collectionName, where, groupField, fields) {
let query = await db.collection(collectionName);
query = await query.aggregate();
// 查询条件
query = await query.match(fmtWhere(where));
if (!Array.isArray(fields)) {
fields = [fields];
}
let node = {};
for (let k = 0; k < fields.length; k++) {
node[fields[k]] = dbAggr.sum('$' + fields[k]);
}
query = await query.group({
_id: {
[groupField]: '$' + groupField
},
...node
}).end();
if (query && query.list) {
let list = query.list;
for (let k = 0; k < list.length; k++) {
list[k][groupField] = list[k]['_id'][groupField];
delete list[k]['_id'];
}
return list;
} else
return [];
}
async function groupCount(collectionName, where, groupField) {
let query = await db.collection(collectionName);
query = await query.aggregate();
// 查询条件
query = await query.match(fmtWhere(where));
query = await query.group({
_id: '$' + groupField,
total: dbAggr.sum(1)
}).end();
if (query && query.list) {
let list = query.list;
let ret = {};
for (let k = 0; k < list.length; k++) {
ret[groupField + '_' + list[k]['_id']] = list[k].total;
}
return ret;
} else
return null;
}
async function sum(collectionName, where, field) {
// TODO 可扩展为支持多个字段同时统计
let query = await db.collection(collectionName);
query = await query.aggregate();
// 查询条件
query = await query.match(fmtWhere(where));
query = await query.group({
_id: null,
'summ': dbAggr.sum('$' + field)
}).end();
if (query && query.list && query.list[0] && query.list[0]['summ']) {
return query.list[0]['summ'];
} else
return 0;
}
async function max(collectionName, where, field) {
let query = await db.collection(collectionName);
query = await query.aggregate();
// 查询条件
query = await query.match(fmtWhere(where));
query = await query.group({
_id: null,
'maxx': dbAggr.max('$' + field)
}).end();
if (query && query.list && query.list[0] && query.list[0]['maxx']) {
return query.list[0]['maxx'];
} else
return 0;
}
async function min(collectionName, where, field) {
let query = await db.collection(collectionName);
query = await query.aggregate();
// 查询条件
query = await query.match(fmtWhere(where));
query = await query.group({
_id: null,
'minx': dbAggr.min('$' + field)
}).end();
if (query && query.list && query.list[0] && query.list[0]['minx']) {
return query.list[0]['minx'];
} else
return 0;
}
async function clear(collectionName) {
await db.collection(collectionName).where({
_id: dbCmd.neq(1)
}).remove().then(res => {
});
}
async function isExistCollection(collectionName) {
try {
await getOne(collectionName, {});
return true;
} catch (err) {
return false;
}
}
async function createCollection(collectionName) {
try {
await db.createCollection(collectionName);
console.log('>> Create New Collection [' + collectionName + '] Succ, OVER.');
return true;
} catch (err) {
console.error('>> Create New Collection [' + collectionName + '] Failed, Code=' + err.errCode + '|' + err.errMsg);
return false;
}
}
async function rand(collectionName, where = {}, fields = '*', size = 1) {
size = Number(size);
if (size > MAX_RECORD_SIZE) size = MAX_RECORD_SIZE;
let query = await db.collection(collectionName)
.aggregate();
if (util.isDefined(where))
query = await query.match(fmtWhere(where));
if (util.isDefined(fields) && fields != '*')
query = await query.project(fmtFields(fields));
query = await query.sample({
size
});
query = await query.end();
if (size == 1) {
if (query.list.length == 1)
return query.list[0];
else
return null;
} else
return query.list;
}
async function getListByArray(collectionName, arrField, where, fields, orderBy, page = 1, size = DEFAULT_RECORD_SIZE, isTotal = true, oldTotal = 0) {
if (typeof (where) == 'string' || typeof (where) == 'number') {
where = {
_id: where,
};
}
page = Number(page);
size = Number(size);
if (size > MAX_RECORD_SIZE) size = MAX_RECORD_SIZE;
data = {
page: page,
size: size
}
let offset = 0; //记录偏移量 防止新增数据列表重复
// 计算总页数
if (isTotal) {
// 联表
let queryCnt = await db.collection(collectionName)
.aggregate();
// 查询条件
if (util.isDefined(where))
queryCnt = await queryCnt.match(fmtWhere(where));
let total = await queryCnt.count('total').end();
if (!total.list.length)
total = 0;
else
total = total.list[0].total;
data.total = total;
data.count = Math.ceil(total / size);
if (page > 1 && oldTotal > 0) {
offset = data.total - oldTotal
if (offset < 0) offset = 0;
}
}
// 拆分查询
let query = await db.collection(collectionName)
.aggregate()
.unwind('$' + arrField);
// 查询条件
if (util.isDefined(where))
query = await query.match(fmtWhere(where));
// 取出特定字段
if (util.isDefined(fields) && fields != '*')
query = await query.project(fmtFields(fields));
// 排序
if (util.isDefined(orderBy)) {
query = await query.sort(fmtJoinSort(orderBy));
}
// 分页
query = await query.skip((page - 1) * size + offset).limit(size);
// 取数据
query = await query.end();
data.list = query.list;
return data;
}
async function getListJoin(collectionName, joinParams, where, fields, orderBy, page = 1, size = DEFAULT_RECORD_SIZE, isTotal = true, oldTotal = 0, is2Many = false) {
if (typeof (where) == 'string' || typeof (where) == 'number') {
where = {
_id: where,
};
}
page = Number(page);
size = Number(size);
if (size > MAX_RECORD_SIZE) size = MAX_RECORD_SIZE;
data = {
page: page,
size: size
}
let offset = 0; //记录偏移量 防止新增数据列表重复
// 计算总页数
if (isTotal) {
// 联表
let queryCnt = await db.collection(collectionName)
.aggregate()
.lookup(joinParams);
// 查询条件
if (util.isDefined(where))
queryCnt = await queryCnt.match(fmtWhere(where));
let total = await queryCnt.count('total').end();
if (!total.list.length)
total = 0;
else
total = total.list[0].total;
data.total = total;
data.count = Math.ceil(total / size);
if (page > 1 && oldTotal > 0) {
offset = data.total - oldTotal
if (offset < 0) offset = 0;
}
}
// 联表
let query = await db.collection(collectionName)
.aggregate()
.lookup(joinParams);
/*
query = await query.replaceRoot({
newRoot: $.mergeObjects([ $.arrayElemAt(['$USER_DETAIL', 0]), '$$ROOT' ])
})*/
// 查询条件
if (util.isDefined(where))
query = await query.match(fmtWhere(where));
// 取出特定字段
if (util.isDefined(fields) && fields != '*')
query = await query.project(fmtFields(fields));
// 排序
if (util.isDefined(orderBy)) {
query = await query.sort(fmtJoinSort(orderBy));
}
// 分页
query = await query.skip((page - 1) * size + offset).limit(size);
// 取数据
query = await query.end();
data.list = query.list;
if (!is2Many) {
for (let k = 0; k < data.list.length; k++) {
if (util.isDefined(data.list[k][joinParams.as])) {
if (Array.isArray(data.list[k][joinParams.as]) &&
data.list[k][joinParams.as].length > 0)
data.list[k][joinParams.as] = data.list[k][joinParams.as][0];
else {
data.list[k][joinParams.as] = {};
}
}
}
}
return data;
}
async function getListJoinCount(collectionName, joinParams, where) {
if (typeof (where) == 'string' || typeof (where) == 'number') {
where = {
_id: where,
};
}
// 联表
let queryCnt = await db.collection(collectionName)
.aggregate()
.lookup(joinParams);
// 查询条件
if (util.isDefined(where))
queryCnt = await queryCnt.match(fmtWhere(where));
let total = await queryCnt.count('total').end();
if (!total.list.length)
total = 0;
else
total = total.list[0].total;
return total;
}
async function getList(collectionName, where, fields = '*', orderBy = {}, page = 1, size = DEFAULT_RECORD_SIZE, isTotal = true, oldTotal = 0) {
page = Number(page);
size = Number(size);
if (size > MAX_RECORD_SIZE || size == 0) size = MAX_RECORD_SIZE;
let data = {
page: page,
size: size
}
let offset = 0;
// 计算总页数
if (isTotal) {
let total = await count(collectionName, where);
data.total = total;
data.count = Math.ceil(total / size);
if (page > 1 && oldTotal > 0) {
offset = data.total - oldTotal
if (offset < 0) offset = 0;
}
}
// 分页
let query = await db.collection(collectionName)
.skip((page - 1) * size + offset)
.limit(size);
// 查询条件
if (util.isDefined(where) && where)
query = await query.where(fmtWhere(where));
// 取出特定字段
if (util.isDefined(fields) && fields != '*')
query = await query.field(fmtFields(fields));
// 排序
if (util.isDefined(orderBy)) {
query = await fmtOrderBy(query, orderBy);
}
// 取数据
query = await query.get();
data.list = query.data;
return data;
}
async function getAllBig(collectionName, where, fields = '*', orderBy, size = MAX_RECORD_SIZE) {
size = Number(size);
if (size > MAX_RECORD_SIZE) size = MAX_RECORD_SIZE;
// 计算总数
let total = await await count(collectionName, where);
// 页数
let page = Math.ceil(total / size);
let list = [];
for (let i = 1; i <= page; i++) {
let data = await getList(collectionName, where, fields, orderBy, i, size, false);
if (data && data.list)
list = list.concat(data.list);
}
return list;
}
async function getAll(collectionName, where, fields = '*', orderBy, size = MAX_RECORD_SIZE) {
size = Number(size);
if (size > MAX_RECORD_SIZE) size = MAX_RECORD_SIZE;
let query = await db.collection(collectionName).limit(size);
// 查询条件
if (where)
query = await query.where(fmtWhere(where));
// 取出特定字段
if (fields && fields != '*')
query = await query.field(fmtFields(fields));
// 排序
if (orderBy) {
query = await fmtOrderBy(query, orderBy);
}
// 取数据
query = await query.get();
return query.data;
}
async function getAllByArray(collectionName, arrField, where, fields = '*', orderBy, size = MAX_RECORD_SIZE) {
size = Number(size);
if (size > MAX_RECORD_SIZE) size = MAX_RECORD_SIZE;
// 拆分
let query = await db.collection(collectionName).aggregate()
.unwind('$' + arrField);
// 查询条件
if (util.isDefined(where))
query = await query.match(fmtWhere(where));
// 取出特定字段
if (util.isDefined(fields) && fields != '*')
query = await query.project(fmtFields(fields));
// 排序
if (util.isDefined(orderBy)) {
query = await query.sort(fmtJoinSort(orderBy));
}
// 取数据
query = await query.limit(size).end();
return query.list;
}
async function getOne(collectionName, where, fields = '*', orderBy = {}) {
if (typeof (where) == 'string' || typeof (where) == 'number') {
where = {
_id: where
};
}
// 查询条件
let query = await db.collection(collectionName)
.where(fmtWhere(where))
.limit(1);
// 取出特定字段
if (fields != '*')
query = await query.field(fmtFields(fields));
// 排序
if (orderBy)
query = await fmtOrderBy(query, orderBy);
// 取数据
query = await query.get();
if (query && query.data.length > 0) {
return query.data[0];
} else
return null;
}
async function fmtOrderBy(query, orderBy) {
for (let key in orderBy) {
query = await query.orderBy(key, orderBy[key].toLowerCase())
}
return query;
}
function fmtJoinSort(sort) {
for (let key in sort) {
let v = sort[key];
if (typeof (v) == 'string') {
v = v.toLowerCase();
if (v === 'asc')
v = 1;
else
v = -1;
}
sort[key] = v;
}
return sort;
}
function fmtFields(fields) {
if (typeof (fields) == 'string') {
let obj = {};
fields = fields.replace(/,/g, ",");
let arr = fields.split(',');
for (let i = 0; i < arr.length; i++) {
if (arr[i].trim().length > 0)
obj[arr[i].trim()] = true;
}
return obj;
}
return fields;
}
function fmtWhere(where) {
if (util.isDefined(where.and) || util.isDefined(where.or)) {
let whereEx = null;
if (util.isDefined(where.and))
whereEx = dbCmd.and(fmtWhere(where.and));
if (util.isDefined(where.or)) {
if (whereEx)
whereEx = whereEx.and(dbCmd.or(fmtWhere(where.or)));
else
whereEx = dbCmd.or(fmtWhere(where.or));
}
//console.log(whereEx);
return whereEx;
}
if (Array.isArray(where)) {
for (let i = 0; i < where.length; i++)
where[i] = fmtWhere(where[i]);
}
for (let key in where) {
/* 判断是否有条件数组
INFO_EXPIRE_TIME: [
['>=', 3],
['<=', 8],
['<>', 5],
['in', '6,7']
],
*/
if (Array.isArray(where[key])) {
let w = null;
if (!Array.isArray(where[key][0]) && where[key][0].toLowerCase().trim() == 'between') {
// 条件查询特殊处理
where[key] = [
['>=', where[key][1]],
['<=', where[key][2]]
];
}
if (!Array.isArray(where[key][0])) {
// 一维数组
w = fmtWhereSimple(where[key]);
} else {
// 二维数组
for (let i = 0; i < where[key].length; i++) {
let wTemp = fmtWhereSimple(where[key][i]);
w = (w) ? w.and(wTemp) : wTemp;
}
}
where[key] = w;
}
}
return where;
}
function fmtWhereSimple(arr) {
let sql = '';
let op = arr[0].toLowerCase().trim();
let val = arr[1];
let where = {};
switch (op) {
case '=':
where = dbCmd.eq(val);
break;
case '!=':
case '<>':
where = dbCmd.neq(val);
break;
case '<':
where = dbCmd.lt(val);
break;
case '<=':
where = dbCmd.lte(val);
break;
case '>':
where = dbCmd.gt(val);
break;
case '>=':
where = dbCmd.gte(val);
break;
case 'like':
if (!util.isDefined(val) || !val) break; //无条件不搜索
where = {
$regex: val,
$options: 'i'
}
break;
case 'in':
val = dataUtil.str2Arr(val);
where = dbCmd.in(val);
break;
case 'not in':
val = dataUtil.str2Arr(val);
where = dbCmd.nin(val);
break;
default:
console.error('error where oprt=' + op);
break;
}
return where;
}
module.exports = {
getCmd,
insert,
insertBatch,
edit,
del,
count,
inc,
sum,
groupCount,
groupSum,
distinct,
distinctCnt,
max,
min,
mul,
isExistCollection,
createCollection,
clear,
rand,
getOne,
getAll,
getAllBig,
getAllByArray,
getList,
getListJoin,
getListJoinCount,
getListByArray,
MAX_RECORD_SIZE,
DEFAULT_RECORD_SIZE
}
================================================
FILE: cloudfunctions/mcloud/framework/database/model.js
================================================
/**
* Notes: 数据持久化与操作模块
* Ver : CCMiniCloud Framework 2.11.1 ALL RIGHTS RESERVED BY ccLinux0730 (wechat)
* Date: 2020-09-04 04:00:00
*/
const dbUtil = require('./db_util.js');
const util = require('../utils/util.js');
const timeUtil = require('../utils/time_util.js');
const dataUtil = require('../utils/data_util.js');
const AppError = require('../core/app_error.js');
const cloudBase = require('../cloud/cloud_base.js');
class Model {
static async getOne(where, fields = '*', orderBy = {}) {
return await dbUtil.getOne(this.CL, where, fields, orderBy);
}
static async edit(where, data) {
if (this.UPDATE_TIME) {
let editField = this.FIELD_PREFIX + 'EDIT_TIME';
if (!util.isDefined(data[editField])) data[editField] = timeUtil.time();
}
if (this.UPDATE_IP) {
let cloud = cloudBase.getCloud();
let ip = cloud.getWXContext().CLIENTIP;
let editField = this.FIELD_PREFIX + 'EDIT_IP';
if (!util.isDefined(data[editField])) data[editField] = ip;
}
data = this.clearEditData(data);
return await dbUtil.edit(this.CL, where, data);
}
static async count(where) {
return await dbUtil.count(this.CL, where);
}
static async insert(data) {
// 自动ID
if (this.ADD_ID) {
let idField = this.FIELD_PREFIX + 'ID';
if (!util.isDefined(data[idField])) data[idField] = dataUtil.makeID();
}
// 更新时间
if (this.UPDATE_TIME) {
let timestamp = timeUtil.time();
let addField = this.FIELD_PREFIX + 'ADD_TIME';
if (!util.isDefined(data[addField])) data[addField] = timestamp;
let editField = this.FIELD_PREFIX + 'EDIT_TIME';
if (!util.isDefined(data[editField])) data[editField] = timestamp;
}
// 更新IP
if (this.UPDATE_IP) {
let cloud = cloudBase.getCloud();
let ip = cloud.getWXContext().CLIENTIP;
let addField = this.FIELD_PREFIX + 'ADD_IP';
if (!util.isDefined(data[addField])) data[addField] = ip;
let editField = this.FIELD_PREFIX + 'EDIT_IP';
if (!util.isDefined(data[editField])) data[editField] = ip;
}
// 数据清洗
data = this.clearCreateData(data);
return await dbUtil.insert(this.CL, data);
}
static async insertOrUpdate(where, data) {
let model = await dbUtil.getOne(this.CL, where, '_id');
if (model) {
await this.edit(model._id, data);
return model._id;
} else {
return await this.insert(Object.assign(data, where));
}
}
static async insertBatch(data = [], size = 1000) {
if (this.ADD_ID) {
let idField = this.FIELD_PREFIX + 'ID';
for (let k = 0; k < data.length; k++) {
if (!util.isDefined(data[k][idField])) data[k][idField] = dataUtil.makeID();
}
}
if (this.UPDATE_TIME) {
let timestamp = timeUtil.time();
let addField = this.FIELD_PREFIX + 'ADD_TIME';
let editField = this.FIELD_PREFIX + 'EDIT_TIME';
for (let k = 0; k < data.length; k++) {
if (!util.isDefined(data[k][addField])) data[k][addField] = timestamp;
if (!util.isDefined(data[k][editField])) data[k][editField] = timestamp;
}
}
if (this.UPDATE_IP) {
let cloud = cloudBase.getCloud();
let ip = cloud.getWXContext().CLIENTIP;
let addField = this.FIELD_PREFIX + 'ADD_IP';
let editField = this.FIELD_PREFIX + 'EDIT_IP';
for (let k = 0; k < data.length; k++) {
if (!util.isDefined(data[k][addField])) data[k][addField] = ip;
if (!util.isDefined(data[k][editField])) data[k][editField] = ip;
}
}
for (let k = 0; k < data.length; k++) {
data[k] = this.clearCreateData(data[k]);
}
return await dbUtil.insertBatch(this.CL, data, size);
}
static async del(where) {
return await dbUtil.del(this.CL, where);
}
static async inc(where, field, val = 1) {
return await dbUtil.inc(this.CL, where, field, val);
}
static async mul(where, field, val = 1) {
return await dbUtil.mul(this.CL, where, field, val);
}
static async groupSum(where, groupField, field) {
return await dbUtil.groupSum(this.CL, where, groupField, field);
}
static async groupCount(where, groupField) {
return await dbUtil.groupCount(this.CL, where, groupField);
}
static async sum(where, field) {
return await dbUtil.sum(this.CL, where, field);
}
static async distinct(where, field) {
return await dbUtil.distinct(this.CL, where, field);
}
static async distinctCnt(where, field) {
return await dbUtil.distinctCnt(this.CL, where, field);
}
static async max(where, field) {
return await dbUtil.max(this.CL, where, field);
}
static async min(where, field) {
return await dbUtil.min(this.CL, where, field);
}
static async clear() {
return await dbUtil.clear(this.CL);
}
static async rand(where = {}, fields = '*', size = 1) {
return await dbUtil.rand(this.CL, where, fields, size);
}
static async getAll(where, fields, orderBy, size = 100) {
return await dbUtil.getAll(this.CL, where, fields, orderBy, size);
}
static async getAllBig(where, fields, orderBy, size = 1000) {
return await dbUtil.getAllBig(this.CL, where, fields, orderBy, size);
}
static async getAllByArray(arrField, where, fields, orderBy, size = 100) {
return await dbUtil.getAllByArray(this.CL, arrField, where, fields, orderBy, size);
}
static async getList(where, fields, orderBy, page, size, isTotal, oldTotal) {
return await dbUtil.getList(this.CL, where, fields, orderBy, page, size, isTotal, oldTotal);
}
static async getListJoin(joinParams, where, fields, orderBy, page = 1, size, isTotal = true, oldTotal = 0, is2Many = false) {
return await dbUtil.getListJoin(this.CL, joinParams, where, fields, orderBy, page, size, isTotal, oldTotal, is2Many);
}
static async getListJoinCount(joinParams, where) {
return await dbUtil.getListJoinCount(this.CL, joinParams, where);
}
static async getListByArray(arrField, where, fields, orderBy, page = 1, size, isTotal = true, oldTotal = 0) {
return await dbUtil.getListByArray(this.CL, arrField, where, fields, orderBy, page, size, isTotal, oldTotal);
}
static converDBStructure(stru) {
let newStru = {};
for (let key in stru) {
newStru[key] = {};
let arr = stru[key].split('|');
for (let k = 0; k < arr.length; k++) {
// 类型
let val = arr[k].toLowerCase().trim();
let orginal = arr[k];
let type = 'string';
if (val === 'float' || val === 'int' || val === 'string' || val === 'array' || val === 'object' || val === 'bool') {
type = val;
newStru[key]['type'] = type;
continue;
}
// 是否必填
if (val === 'true' || val === 'false') {
let required = (val === 'true') ? true : false;
newStru[key]['required'] = required;
continue;
}
// 默认值
if (val.startsWith('default=') && util.isDefined(newStru[key]['type'])) {
let defVal = orginal.replace('default=', '');
switch (newStru[key]['type']) {
case 'int':
case 'float':
defVal = Number(defVal);
break;
case 'bool':
defVal = defVal.toLowerCase();
defVal = defVal == 'true' ? true : false;
break;
case 'object':
defVal = defVal.replace('{', '');
defVal = defVal.replace('}', '').trim();
if (!defVal)
defVal = {};
else {
let arr = defVal.split(',');
defVal = {};
for (let m = 0; m < arr.length; m++) {
if (arr[m]) defVal[arr[m]] = '';
}
}
break;
case 'array':
defVal = defVal.replace('[', '');
defVal = defVal.replace(']', '').trim();
if (!defVal)
defVal = [];
else
defVal = defVal.split(',');
break;
default:
defVal = String(defVal);
}
newStru[key]['defVal'] = defVal;
continue;
}
// 注释
if (val.startsWith('comment=')) {
let comment = orginal.replace('comment=', '');
newStru[key]['comment'] = comment;
continue;
}
// 长度
if (val.startsWith('length=')) {
let length = orginal.replace('length=', '');
length = Number(length);
newStru[key]['length'] = length;
continue;
}
}
// 如果非必填字段没有默认值,则主动赋予一个
if (!newStru[key]['required'] && !util.isDefined(newStru[key]['defVal'])) {
let defVal = '';
switch (newStru[key]['type']) {
case 'bool':
defVal = false;
break;
case 'int':
case 'float':
defVal = Number(0);
break;
case 'array':
defVal = [];
break;
case 'object':
defVal = {};
break;
default:
defVal = String('');
}
newStru[key]['defVal'] = defVal;
}
// 如果没有长度
if (!util.isDefined(newStru[key]['length'])) {
let length = 20;
switch (newStru[key]['type']) {
case 'int':
case 'float':
length = 30;
break;
case 'array':
case 'object':
length = 1500;
break;
default:
length = 300;
}
newStru[key]['length'] = length;
}
}
return newStru;
}
static clearDirtyData(data) {
for (let key in data) {
if (!this.DB_STRUCTURE.hasOwnProperty(key) && !key.includes('.')) {
console.error('脏数据:' + key);
throw new AppError('脏数据');
}
}
}
static converDataType(data, dbStructure) {
for (let key in data) {
if (dbStructure.hasOwnProperty(key)) {
let type = dbStructure[key].type.toLowerCase();
// 字段类型转换
switch (type) {
case 'string':
data[key] = String(data[key]);
break;
case 'bool':
//data[key] = data[key];
break;
case 'float':
case 'int':
data[key] = Number(data[key]);
break;
case 'array':
if (data[key].constructor != Array)
data[key] = [];
break;
case 'object':
if (data[key] === undefined || data[key] === null) {
data[key] = {};
}
else if (data[key].constructor != Object)
data[key] = {};
//data[key] = dbUtil.getCmd().set(data[key]); // 指定字段等于一个对象 TODO
break;
default:
console.error('字段类型错误:' + key + dbStructure[key].type);
throw new AppError("字段类型错误");
}
}
}
return data;
}
static clearCreateData(data) {
let dbStructure = Model.converDBStructure(this.DB_STRUCTURE);
for (let key in dbStructure) {
// 数据类型
if (!util.isDefined(dbStructure[key].type)) {
console.log('[数据填写错误1]字段类型未定义:' + key);
throw new AppError('数据填写错误1');
}
// 是否定义必填
if (!util.isDefined(dbStructure[key].required)) {
console.log('[数据填写错误2]required未定义:' + key);
throw new AppError('数据填写错误2');
}
// 键值未赋值情况
if (!data.hasOwnProperty(key)) {
// 必填
if (dbStructure[key].required) {
if (util.isDefined(dbStructure[key].defVal))
// 必填且有缺省值
data[key] = dbStructure[key].defVal;
else {
// 必填且无缺省值
console.log('[数据填写错误3]字段未填写:' + key);
throw new AppError('数据填写错误3 ' + key);
}
} else {
// 非必填字段必须有缺省值
if (!util.isDefined(dbStructure[key].defVal)) {
console.log('[数据填写错误4]非必填字段必须有缺省值:' + key);
throw new AppError('数据填写错误4');
}
data[key] = dbStructure[key].defVal;
}
}
}
// 去掉脏数据
this.clearDirtyData(data, dbStructure);
data = this.converDataType(data, dbStructure);
return data;
}
static clearEditData(data) {
let dbStructure = Model.converDBStructure(this.DB_STRUCTURE);
// 去掉脏数据
this.clearDirtyData(data, dbStructure);
data = this.converDataType(data, dbStructure);
return data;
}
static getDesc(enumName, val) {
let baseEnum = this[enumName];
let descEnum = this[enumName + '_DESC']
let enumKey = '';
// 先找出KEY
for (let key in baseEnum) {
if (baseEnum[key] === val) {
enumKey = key;
break;
}
}
if (enumKey == '') return 'unknown';
// 再从Desc里找出描述
return descEnum[enumKey];
}
}
// 集合名 collection
Model.CL = 'no-collection';
// 集合结构
Model.DB_STRUCTURE = 'no-dbStructure';
// 字段前缀
Model.FIELD_PREFIX = 'NO_';
// 开关自带更新ADD_TIME,EDIT_TIME,DEL_TIME的操作
Model.UPDATE_TIME = true;
// 开关自带更新ADD_IP,EDIT_IP,DEL_IP的操作
Model.UPDATE_IP = true;
// 开关添加ID
Model.ADD_ID = true;
// 默认排序
Model.ORDER_BY = {
_id: 'desc'
}
module.exports = Model;
================================================
FILE: cloudfunctions/mcloud/framework/database/multi_model.js
================================================
/**
* Notes: 实体基类
* Date: 2021-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.12.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const Model = require('./model.js');
const util = require('../utils/util.js');
const dataUtil = require('../utils/data_util.js');
const config = require('../../config/config.js');
class MultiModel extends Model {
static C(cl) {
return config.COLLECTION_PRFIX + cl;
}
static _getWhere(where, mustPID = true) {
if (mustPID) {
if (typeof (where) == 'string' || typeof (where) == 'number') {
where = {
_id: where,
_pid: util.getProjectId()
};
} else
where._pid = util.getProjectId();
}
return where;
}
static async getOneField(where, field, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
let one = await super.getOne(where, field);
if (!one)
return null;
else {
let ret = one[field];
if (ret === undefined)
return '';
else
return one[field];
}
}
static async getOne(where, fields = '*', orderBy = {}, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.getOne(where, fields, orderBy);
}
static async edit(where, data, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.edit(where, data);
}
static async editForms(where, formName, objName, hasImageForms, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
let forms = await this.getOneField(where, formName, mustPID);
if (!forms) return;
// 赋值
for (let k = 0; k < hasImageForms.length; k++) {
for (let j in forms) {
if ((forms[j].type == 'image' || forms[j].type == 'content')
&& forms[j].mark == hasImageForms[k].mark
&& forms[j].type == hasImageForms[k].type) {
forms[j].val = hasImageForms[k].val;
break;
}
}
}
let data = {
[formName]: forms,
[objName]: dataUtil.dbForms2Obj(forms)
};
return await super.edit(where, data);
}
static async count(where, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.count(where);
}
static async insert(data, mustPID = true) {
if (mustPID) data._pid = util.getProjectId();
return await super.insert(data);
}
static async insertBatch(data = [], size = 1000, mustPID = true) {
if (mustPID) {
for (let k = 0; k < data.length; k++) {
data[k]._pid = util.getProjectId();
}
}
return await super.insertBatch(data, size);
}
static async insertOrUpdate(where, data, mustPID = true) {
if (mustPID) {
where._pid = util.getProjectId();
}
return await super.insertOrUpdate(where, data);
}
static async del(where, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.del(where);
}
static async clear() {
return await super.clear();
}
static async inc(where, field, val = 1, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.inc(where, field, val);
}
static async mul(where, field, val = 1, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.mul(where, field, val);
}
static async groupSum(where, groupField, field, mustPID = true) {
if (mustPID) where._pid = util.getProjectId();
return await super.groupSum(where, groupField, field);
}
static async groupCount(where, groupField, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.groupCount(where, groupField);
}
static async sum(where, field, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.sum(where, field);
}
static async distinct(where, field, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.distinct(where, field);
}
static async distinctCnt(where, field, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.distinctCnt(where, field);
}
static async max(where, field, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.max(where, field);
}
static async min(where, field, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.min(where, field);
}
static async rand(where, field, size = 1, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.rand(where, field, size);
}
static async getAll(where, fields, orderBy, size = 100, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.getAll(where, fields, orderBy, size);
}
static async getAllBig(where, fields, orderBy, size = 1000, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.getAllBig(where, fields, orderBy, size);
}
static async getAllByArray(arrField, where, fields, orderBy, size = 100, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.getAllByArray(arrField, where, fields, orderBy, size);
}
static async getList(where, fields, orderBy, page, size, isTotal, oldTotal, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
static async getListJoin(joinParams, where, fields, orderBy, page = 1, size, isTotal = true, oldTotal = 0, is2Many = false, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.getListJoin(joinParams, where, fields, orderBy, page, size, isTotal, oldTotal, is2Many);
}
static async getListJoinCount(joinParams, where, mustPID = true) {
where = MultiModel._getWhere(where, mustPID);
return await super.getListJoinCount(joinParams, where);
}
}
module.exports = MultiModel;
================================================
FILE: cloudfunctions/mcloud/framework/lib/faker_lib.js
================================================
/**
* Notes: 测试数据构造类
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-05-26 14:00:00
*/
const timeUtil = require('../utils/time_util.js');
/** 随机获取数据 */
function getRnd(arr, isNullable = false, ex = '') {
if (isNullable) { // 允许为空
let rd = getIntBetween(0, 1);
if (rd % 2 == 0) return '';
}
if (!Array.isArray(arr)) {
arr = arr.replace(/ /g, '').replace(/,/g, ',').split(',');
}
let exArr = ex.replace(/ /g, '').replace(/,/g, ',').split(',');
let ret = '';
let i = 0;
while (true) {
i++;
if (i > 1000) return '';
ret = arr[Math.floor((Math.random() * arr.length))];
if (!exArr.includes(ret))
return ret;
}
}
/** 省份 */
function getProvince(isNullable = false, ex = '') {
let data = ['北京市', '天津市', '河北省', '山西省',
'内蒙古自治区', '辽宁省', '吉林省',
'黑龙江省', '上海市', '江苏省',
'浙江省', '安徽省', '福建省', '江西省',
'山东省', '河南省', '湖北省', '湖南省',
'广东省', '广西壮族自治区', '海南省',
'重庆市', '四川省', '贵州省', '云南省',
'西藏自治区', '陕西省', '甘肃省', '青海省',
'宁夏回族自治区', '新疆维吾尔自治区',
'香港特别行政区', '澳门特别行政区', '台湾省'
];
return getRnd(data, isNullable, ex);
}
function getProvinceAbbr(isNullable = false, ex = '') {
let data = ['京', '皖', '渝', '闽',
'甘', '粤', '桂', '黔',
'琼', '冀', '豫', '黑',
'鄂', '湘', '吉', '苏',
'赣', '辽', '蒙', '宁',
'青', '鲁', '晋', '陕',
'沪', '川', '津', '藏',
'新', '滇', '浙', '港',
'澳', '台'
];
return getRnd(data, isNullable, ex);
}
/** 城市 */
function getCity(isNullable = false, ex = '') {
let data = ['北京', '上海', '天津', '重庆',
'哈尔滨', '长春', '沈阳', '呼和浩特',
'石家庄', '乌鲁木齐', '兰州', '西宁',
'西安', '银川', '郑州', '济南',
'太原', '合肥', '武汉', '长沙',
'南京', '成都', '贵阳', '昆明',
'南宁', '拉萨', '杭州', '南昌',
'广州', '福州', '海口',
'香港', '澳门'
];
return getRnd(data, isNullable, ex);
}
/** 地区 */
function getArea(isNullable = false, ex = '') {
let data = ['西夏区', '永川区', '秀英区', '高港区',
'清城区', '兴山区', '锡山区', '清河区',
'龙潭区', '华龙区', '海陵区', '滨城区',
'东丽区', '高坪区', '沙湾区', '平山区',
'城北区', '海港区', '沙市区', '双滦区',
'长寿区', '山亭区', '南湖区', '浔阳区',
'南长区', '友好区', '安次区', '翔安区',
'沈河区', '魏都区', '西峰区', '萧山区',
'金平区', '沈北新区', '孝南区', '上街区',
'城东区', '牧野区', '大东区', '白云区',
'花溪区', '吉利区', '新城区', '怀柔区',
'六枝特区', '涪城区', '清浦区', '南溪区',
'淄川区', '高明区', '金水区', '中原区',
'高新开发区', '经济开发新区', '新区'
];
return getRnd(data, isNullable, ex);
}
/** 街道 */
function getStreet(isNullable = false, ex = '') {
let data = '朱雀大街,太乙路,太白路,太华路,长乐坊,长樱路,案板街,竹笆市,骡马市,东木头市,西木头市,安仁坊,端履门,德福巷,洒金桥,冰窖巷,菊花园,下马陵,粉巷,索罗巷,后宰门,书院门,炭市街,马厂子,景龙池,甜水井,柏树林,桃花坞,人民路,解放路,黄河路,长江路,中山路,抚顺街,天津街,上海路,胜利路,西安路,长春路,太原街,沈阳路,鞍山路,五四路,唐山街,武汉街,延安路,朝阳街,鲁迅路,八一路,东北路,华南路,华北路,山东路,松江路,东方路,南沙街';
return getRnd(data, isNullable, ex);
}
/** 门牌地址 */
function getAddress() {
return getProvince() + '' + getCity() + '市' + getArea() + '' + getStreet() + getIntBetween(1, 100) + '号';
}
/** 国家 */
function getCountry(isNullable = false, ex = '') {
let data = ['阿富汗', '阿拉斯加', '阿尔巴尼亚', '阿尔及利亚',
'安道尔', '安哥拉', '安圭拉岛英', '安提瓜和巴布达',
'阿根廷', '亚美尼亚', '阿鲁巴岛', '阿森松', '澳大利亚',
'奥地利', '阿塞拜疆', '巴林', '孟加拉国', '巴巴多斯',
'白俄罗斯', '比利时', '伯利兹', '贝宁', '百慕大群岛',
'不丹', '玻利维亚', '波斯尼亚和黑塞哥维那', '博茨瓦纳',
'巴西', '保加利亚', '布基纳法索', '布隆迪', '喀麦隆',
'加拿大', '加那利群岛', '佛得角', '开曼群岛', '中非',
'乍得', '智利', '圣诞岛', '科科斯岛', '哥伦比亚',
'巴哈马国', '多米尼克国', '科摩罗', '刚果', '科克群岛',
'哥斯达黎加', '克罗地亚', '古巴', '塞浦路斯', '捷克',
'丹麦', '迪戈加西亚岛', '吉布提', '多米尼加共和国',
'厄瓜多尔', '埃及', '萨尔瓦多', '赤道几内亚',
'厄立特里亚', '爱沙尼亚', '埃塞俄比亚', '福克兰群岛',
'法罗群岛', '斐济', '芬兰', '法国', '法属圭亚那',
'法属波里尼西亚', '加蓬', '冈比亚', '格鲁吉亚', '德国',
'加纳', '直布罗陀', '希腊', '格陵兰岛', '格林纳达',
'瓜德罗普岛', '关岛', '危地马拉', '几内亚', '几内亚比绍',
'圭亚那', '海地', '夏威夷', '洪都拉斯', '匈牙利', '冰岛',
'印度', '印度尼西亚', '伊郎', '伊拉克', '爱尔兰', '以色列',
'意大利', '科特迪瓦', '牙买加', '日本', '约旦', '柬埔塞',
'哈萨克斯坦', '肯尼亚', '基里巴斯', '朝鲜', '韩国', '科威特',
'吉尔吉斯斯坦', '老挝', '拉脱维亚', '黎巴嫩', '莱索托',
'利比里亚', '利比亚', '列支敦士登', '立陶宛', '卢森堡',
'马其顿', '马达加斯加', '马拉维', '马来西亚', '马尔代夫',
'马里', '马耳他', '马里亚纳群岛', '马绍尔群岛', '马提尼克',
'毛里塔尼亚', '毛里求斯', '马约特岛', '墨西哥', '密克罗尼西亚',
'中途岛', '摩尔多瓦', '摩纳哥', '蒙古', '蒙特塞拉特岛',
'摩洛哥', '莫桑比克', '缅甸', '纳米比亚', '瑙鲁', '尼泊尔',
'荷兰', '荷属安的列斯群岛', '新喀里多尼亚群岛', '新西兰',
'尼加拉瓜', '尼日尔', '尼日利亚', '纽埃岛', '诺福克岛',
'挪威', '阿曼', '帕劳', '巴拿马', '巴布亚新几内亚', '巴拉圭',
'秘鲁', '菲律宾', '波兰', '葡萄牙', '巴基斯坦', '波多黎各',
'卡塔尔', '留尼汪岛', '罗马尼亚', '俄罗斯', '卢旺达',
'东萨摩亚', '西萨摩亚', '圣马力诺', '圣皮埃尔岛及密克隆岛',
'圣多美和普林西比', '沙特阿拉伯', '塞内加尔', '塞舌尔',
'新加坡', '斯洛伐克', '斯洛文尼亚', '所罗门群岛', '索马里',
'南非', '西班牙', '斯里兰卡', '圣克里斯托弗和尼维斯',
'圣赫勒拿', '圣卢西亚', '圣文森特岛', '苏丹', '苏里南',
'斯威士兰', '瑞典', '瑞士', '叙利亚', '塔吉克斯坦', '坦桑尼亚',
'泰国', '阿拉伯联合酋长国', '多哥', '托克劳群岛', '汤加',
'特立尼达和多巴哥', '突尼斯', '土耳其', '土库曼斯坦',
'特克斯和凯科斯群岛(', '图瓦卢', '美国', '乌干达', '乌克兰',
'英国', '乌拉圭', '乌兹别克斯坦', '瓦努阿图', '梵蒂冈',
'委内瑞拉', '越南', '维尔京群岛', '维尔京群岛和圣罗克伊',
'威克岛', '瓦里斯和富士那群岛', '西撒哈拉', '也门', '南斯拉夫',
'扎伊尔', '赞比亚', '桑给巴尔', '津巴布韦', '中华人民共和国', '中国'
];
return getRnd(data, isNullable, ex);
}
/** 公司简称 */
function getCompanyPrefix(isNullable = false, ex = '') {
let data = ['超艺', '和泰', '九方', '鑫博腾飞', '戴硕电子',
'济南亿次元', '海创', '创联世纪', '凌云', '泰麒麟',
'彩虹', '兰金电子', '晖来计算机', '天益', '恒聪百汇',
'菊风公司', '惠派国际公司', '创汇', '思优', '时空盒数字',
'易动力', '飞海科技', '华泰通安', '盟新', '商软冠联',
'图龙信息', '易动力', '华远软件', '创亿', '时刻',
'开发区世创', '明腾', '良诺', '天开', '毕博诚', '快讯',
'凌颖信息', '黄石金承', '恩悌', '雨林木风计算机',
'双敏电子', '维旺明', '网新恒天', '数字100', '飞利信',
'立信电子', '联通时科', '中建创业', '新格林耐特',
'新宇龙信息', '浙大万朋', 'MBP软件', '昂歌信息',
'万迅电脑', '方正科技', '联软', '七喜', '南康', '银嘉',
'巨奥', '佳禾', '国讯', '信诚致远', '浦华众城', '迪摩',
'太极', '群英', '合联电子', '同兴万点', '襄樊地球村',
'精芯', '艾提科信', '昊嘉', '鸿睿思博', '四通', '富罳',
'商软冠联', '诺依曼软件', '东方峻景', '华成育卓', '趋势',
'维涛', '通际名联'
];
return getRnd(data, isNullable, ex);
}
/** 公司类型 */
function getCompanyType(isNullable = false, ex = '') {
let data = ['科技', '网络', '信息', '传媒', '集团', '控股', '投资', '制造'];
return getRnd(data, isNullable, ex);
}
/** 公司名 */
function getCompany(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
return getCompanyPrefix(false, ex) + getCompanyType() + '有限公司';
}
/** 内容 */
function getContent(size = 1, isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let data = [
'燕舞,燕舞,一曲歌来一片情。',
'康师傅方便面,好吃看得见。',
'不要太潇洒!',
'让一亿人先聪明起来。',
'共创美的前程,共度美的人生。',
'省优,部优,葛优?',
'喝孔府宴酒,做天下文章。',
'健康成就未来。',
'牙好,胃口就好,身体倍儿棒,吃嘛嘛香。',
'永远的绿色,永远的秦池。',
'坐红旗车,走中国路。',
'要想皮肤好,早晚用大宝。',
'孔府家酒,叫人想家。',
'补钙新观念,吸收是要害。',
'喝汇源果汁,走健康之路。',
'爱的就是你!',
'一种可以世袭的古典浪漫',
'实力创造价值',
'爱生活,爱拉芳!',
'人类失去联想,世界将会怎样?',
'做女人挺好!',
'世界在你眼中?',
'今天你有否亿唐?',
'只溶在口,不溶在手。',
'三千烦恼丝,健康新开始。',
'维维豆奶,欢乐开怀。',
'我们的光彩来自你的风采。',
'钻石恒久远,一颗永流传。',
'放我的真心在你的手心。',
'小身材,大味道。',
'牛奶香浓,丝般感受。',
'聆听并不代表沉默,有时安静也是一种力量。',
'滴滴香浓,意犹未尽。',
'水晶之恋,一生不变。',
'中国移动通信,沟通从心开始!',
'网易,网聚人的力量!',
'科技以人为本,诺基亚',
'我们一直在努力!',
'阳光总在风雨后',
'男人对西服的要求,就是女人对男人的要求',
'晚报,不晚报',
'原来生活可以更美的',
'明天的明天,你还会送我“水晶之恋”吗?',
'卫浴出出进进的快感',
'有家就有联合利华',
'减脂减肥,其实是一种生活态度',
'人头马一开,好事自然来。',
'假如五指一样长,怎能满足用户不同需求?',
'新飞广告做的好,不好新飞冰箱好',
'传奇品质,百年张裕',
'李宁:把出色留给自己',
'一旦拥有,别无选择',
'科技让你更轻松',
'情系中国结,联通四海心',
'海尔,中国造',
'SOHU:足迹生活每一天',
'果冻我要喜之郎',
'国宝大熊猫,心纯天自高',
'世界因为不同',
'放低偏见,你会有出色发现!',
'Just',
'创意似金,敬业如牛',
'不要让男人一手把握',
'如同情人的手',
'金窝银窝,不如自己的安乐窝。',
'没有什么大不了的',
'时间因我存在',
'只要有梦想',
'南方周末',
'时间改变一切',
'地球人都知道了',
'众里寻他千百度,想要几度就几度',
'您身边的银行,可信赖的银行',
'三叶钢琴:学琴的孩子不会变坏',
'柯达:串起生活每一刻',
'大众甲克虫汽车:想想还是小的好',
'一直被模拟,从未被超越',
'幸福生活',
'朗讯的创造力科技的原动力',
'事事因你而出色',
'运动之美,世界共享',
'鹤舞白沙',
'想知道“清嘴”的味道吗?',
'弹指一挥间,世界皆互联',
'更多选择、更多欢笑',
'方太,让家的感觉更好',
'世上仅此一件,今生与你结缘!',
'白里透红与众不同',
'没有蛀牙-佳洁士',
'有线的价值',
'享受快乐科技',
'四海一家的解决之道',
'娃哈哈纯净水:爱你等于爱自己',
'农民山泉:有点甜',
'博大精深,西门子',
'一切尽在把握',
'声声百思特,遥遥两相知',
'一呼天下应',
'让我们做得更好!',
'暖和亲情,金龙鱼的大家庭。',
'自然最健康,绿色好心情',
'支起网络世界',
'立邦漆:处处放光彩!',
'fm365:真情互动!',
'庄重一生,吉祥一生。',
'人人都为礼品愁,我送北极海狗油。',
'假如说人生的离合是一场戏,那么百年的好合更是早有安排!',
'一品黄山天高云淡',
'上上下下的享受!',
'我是、我行、我素',
'让无力者有力,让悲观者前行',
'金利来—-男人的世界!',
'百衣百顺',
'聪明何必绝顶,慧根长留',
'水往高处流',
'大石化小,小石化了!',
'“闲”妻良母',
'“口服”,“心服”!',
'盛满青春的秘密!',
'三十六计走为上',
'为了她的节日,献上您纯金般的心!',
'用我们的钓线,你可以在鱼儿发现你之前先找到它',
'生活就是一场运动,喝下它。',
'选择维聚阿尔,已经表明你心明眼亮。',
'佳能,我们看得见你想表达什么。',
'天天都是春天',
'假如你不来,广告明星就是他',
'享受黑夜中偷拍的快感!',
'彩信发送动人一刻',
'灵感点亮生活!',
'聪明演绎,无处不在!',
'事业我一定争取,对你我从未放弃!',
'波导手机,手机中的战斗机',
'鄂尔多斯羊绒衫暖和全世界',
'洁婷245再大的动作也不要紧',
'做光明的牛,产光明的奶',
'假如你的汽车会游泳的话,请照直开,不必刹车。',
'永远要让驾驶执照比你自己先到期。',
'请记住,上帝并不是十全十美的,它给汽车预备了备件,而人没有。',
'小别意酸酸,欢聚心甜甜。',
'除钞票外,承印一切。',
'更多欢乐,更多选择',
'美由你做主',
'由我天地宽',
'Sun是太阳,Java是月亮。',
'不断创新,因为专心',
'趁早下『斑』,请勿『痘』留。',
'创新就是生活',
'有一个漂亮的地方,万科四季花城',
'建筑无限生活',
'臭名远扬,香飘万里',
'尝尝欢笑,经常麦当劳',
'深入成就深度',
'出色湖南,红网了然!',
'因为网络,地球如村!',
'一种质感',
'恒久期盼',
'繁荣民族文化',
'不信,死给你看!',
'天生的,强生的',
'雪津啤酒,真情的味道!',
'听世界,打天下',
'雅芳比女人更了解女人',
'Sun是太阳,Java是月亮。',
'中国网通',
'无线你的无限',
'家有三洋,冬暖夏凉',
'倾诉冬日暖语',
'谁让我心动?',
'灵活,让篮球场不再是一个平面',
'别吻我,我怕修。',
'一呼四应!',
'无所不包!',
'当之无愧',
'以帽取人!',
'一毛不拔!',
'自讨苦吃!',
'成功与科技共辉映',
'没有最',
];
if (size == 1)
return getRnd(data, false, ex);
else {
ret = '';
for (let i = 0; i < size; i++) {
ret += getRnd(data, false, ex) + ', ';
}
return ret;
}
}
/** 获得一句话 */
function getWord(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let ret = getContent(1, false, ex);
ret = ret.replace('。', '').replace('!', '').replace('?', '').replace('“', '”').replace(':', '');
return ret;
}
/** 星期 */
function getWeek(isNullable = false, ex = '') {
let data = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
return getRnd(data, isNullable, ex);
}
/** 月份 */
function getMonth(isNullable = false, ex = '') {
let data = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
return getRnd(data, isNullable, ex);
}
/** 获得姓名 */
function getFirstName(isNullable = false, ex = '') {
let data = ['李', '王', '张', '刘', '陈', '杨', '赵', '黄', '周', '吴',
'徐', '孙', '胡', '朱', '高', '林', '何', '郭', '马', '罗',
'梁', '宋', '郑', '谢', '韩', '唐', '冯', '于', '董', '萧',
'程', '曹', '袁', '邓', '许', '傅', '沉', '曾', '彭', '吕',
'苏', '卢', '蒋', '蔡', '贾', '丁', '林', '薛', '叶', '阎',
'余', '潘', '杜', '戴', '夏', '钟', '汪', '田', '任', '姜',
'范', '方', '石', '姚', '谭', '廖', '邹', '熊', '金', '陆',
'郝', '孔', '白', '崔', '康', '毛', '邱', '秦', '江', '史',
'顾', '侯', '邵', '孟', '龙', '万', '段', '雷', '钱', '汤',
'尹', '黎', '易', '常', '武', '乔', '贺', '赖', '龚', '文',
'庞', '樊', '兰', '殷', '施', '陶', '洪', '翟', '安', '颜',
'倪', '严', '牛', '温', '芦', '季', '俞', '章', '鲁', '葛',
'伍', '韦', '申', '尤', '毕', '聂', '丛', '焦', '向', '柳',
'邢', '路', '岳', '齐', '沿', '梅', '莫', '庄', '辛', '管',
'祝', '左', '涂', '谷', '祁', '时', '舒', '耿', '牟', '卜',
'路', '詹', '关', '苗', '凌', '费', '纪', '靳', '盛', '童',
'欧', '甄', '项', '曲', '成', '游', '阳', '裴', '席', '卫',
'查', '屈', '鲍', '位', '覃', '霍', '翁', '隋', '植', '甘',
'景', '薄', '单', '包', '司', '柏', '宁', '柯', '阮', '桂',
'闵', '欧阳', '解', '强', '柴', '华', '车', '冉', '房', '边',
'辜', '吉', '饶', '刁', '瞿', '戚', '丘', '古', '米', '池',
'滕', '晋', '苑', '邬', '臧', '畅', '宫', '来', '嵺', '苟',
'全', '褚', '廉', '简', '娄', '盖', '符', '奚', '木', '穆',
'党', '燕', '郎', '邸', '冀', '谈', '姬', '屠', '连', '郜',
'晏', '栾', '郁', '商', '蒙', '计', '喻', '揭', '窦', '迟',
'宇', '敖', '糜', '鄢', '冷', '卓', '花', '仇', '艾', '蓝',
'都', '巩', '稽', '井', '练', '仲', '乐', '虞', '卞', '封',
'竺', '冼', '原', '官', '衣', '楚', '佟', '栗', '匡', '宗',
'应', '台', '巫', '鞠', '僧', '桑', '荆', '谌', '银', '扬',
'明', '沙', '薄', '伏', '岑', '习', '胥', '保', '和', '蔺'
];
return getRnd(data, isNullable, ex);
}
/** 女生名 */
function getFemaleName(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let data = ['芳', '娜', '敏', '静', '敏静', '秀英', '丽', '洋', '艳', '娟',
'文娟', '君', '文君', '珺', '霞', '明霞', '秀兰', '燕', '芬', '桂芬',
'玲', '桂英', '丹', '萍', '华', '红', '玉兰', '桂兰', '英', '梅',
'莉', '秀珍', '雪', '依琳', '旭', '宁', '婷', '馨予', '玉珍', '凤英',
'晶', '欢', '玉英', '颖', '红梅', '佳', '倩', '琴', '兰英', '云',
'洁', '爱华', '淑珍', '春梅', '海燕', '晨', '冬梅', '秀荣', '瑞', '桂珍',
'莹', '秀云', '桂荣', '秀梅', '丽娟', '婷婷', '玉华', '琳', '雪梅', '淑兰',
'丽丽', '玉', '秀芳', '欣', '淑英', '桂芳', '丽华', '丹丹', '桂香', '淑华',
'秀华', '桂芝', '小红', '金凤', '文', '利', '楠', '红霞', '瑜', '桂花',
'璐', '凤兰', '腊梅', '瑶', '嘉', '怡', '冰冰', '玉梅', '慧', '婕'
];
return getFirstName(false, ex) + getRnd(data, false, ex);
}
/** 男生名 */
function getMaleName(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let data = ['伟', '强', '磊', '洋', '勇', '军', '杰', '涛', '超', '明',
'刚', '平', '辉', '鹏', '华', '飞', '鑫', '波', '斌', '宇',
'浩', '凯', '健', '俊', '帆', '帅', '旭', '宁', '龙', '林',
'欢', '阳', '建华', '亮', '成', '畅', '建', '峰', '建国', '建军',
'晨', '瑞', '志强', '兵', '雷', '东', '欣', '博', '彬', '坤',
'全安', '荣', '岩', '杨', '文', '利', '楠', '建平', '嘉俊', '晧',
'建明', '子安', '新华', '鹏程', '学明', '博涛', '捷', '文彬', '楼', '鹰',
'松', '伦', '超', '钟', '瑜', '振国', '洪', '毅', '昱然', '哲',
'翔', '翼', '祥', '国庆', '哲彦', '正诚', '正豪', '正平', '正业', '志诚',
'志新', '志勇', '志明', '志强', '志文', '致远', '智明', '智勇', '智敏', '智渊'
];
return getFirstName(false, ex) + getRnd(data, false, ex);
}
/** 随机获得姓名 */
function getName(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let rd = Math.round(Math.random());
return (rd % 2 == 0) ? getFemaleName(false, ex) : getMaleName(false, ex);
}
/** 身份证号码 */
function getIdCard(birthday = '', isNullable = false) {
if (getNullable(isNullable)) return '';
let coefficientArray = ["7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", "9", "10", "5", "8", "4", "2"]; // 加权因子
let lastNumberArray = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]; // 校验码
let address = "420101"; // 住址
if (!birthday)
birthday = "19810101"; // 生日
let s = Math.floor(Math.random() * 10).toString() + Math.floor(Math.random() * 10).toString() + Math.floor(Math.random() * 10).toString();
let array = (address + birthday + s).split("");
let total = 0;
for (i in array) {
total = total + parseInt(array[i]) * parseInt(coefficientArray[i]);
}
let lastNumber = lastNumberArray[parseInt(total % 11)];
let str = address + birthday + s + lastNumber;
return str;
}
/** 手机号码 */
function getMobile(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let data = ['133', '149', '153', '173', '177', '180', '181', '189', '190', '191', '193', '199', '130', '131', '132', '145', '155', '156', '166', '167', '171', '175', '176', '185', '186', '196', '134', '135', '136', '137', '138', '139', '147', '148', '150', '151', '152', '157', '158', '159', '172', '178', '182', '183', '184', '187', '188', '195', '197', '198'];
return getRnd(data, false, ex) + getInt(8);
}
/** 电话号码 */
function getPhone(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let data = ['010', '021', '022', '023', '020', '024', '025', '027', '028', '029', '0755', '0731', '0769'];
return getRnd(data, false, ex) + '-' + getInt(8);
}
/** 常用英文单词 */
function getEnWord(isNullable = false, ex = '') {
let data = 'earthday,org,suggests,that,every,household,take,time,this,earth,day,to,perform,a,plastic,audit,which,involves,counting,how,many,plastic,containers,wraps,bottles,and,bags,are,purchased,for,at,home,use,it,may,surprise,you,how,many,you,use,until,you,start,counting,while,were,not,saying,that,you,have,to,get,rid,of,every,single,ounce,of,plastic,in,your,home,it,is,important,to,be,aware,of,your,familys,plastic,usage,and,to,take,time,to,research,more,sustainable,products,and,start,to,incorporate,them,into,your,daily,life,simple,swaps2,like,glass,containers,instead,of,plastic,or,stainless3,steel,bottles,instead,of,single,use,plastics,can,go,a,long,way,to,making,a,difference';
return getRnd(data, isNullable, ex);
}
/** 常用域名 */
function getDomain(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let data = 'com,net,org,cn,hk,us,uk,jp,kr';
return '.' + getRnd(data, false, ex);
}
/** 常用邮箱 */
function getEmail(isNullable = false, ex = '') {
if (getNullable(isNullable)) return '';
let data = 'qq.com,163.com,gmail.com,263.com,tom.com,163.net,189.cn,sina.com,sohu.com,360.com,tencent.com,china.com,netease.com,126.com,139.com';
return getEnWord() + '@' + getRnd(data, false, ex);
}
/** 获取时间戳 step 秒 */
function getTimestamp(step = 0) {
return timeUtil.time() + step * 1000;
}
/**
* 以当天为基点,获取随机时间戳,默认为当天
* @param {*} min 起始天
* @param {*} max 终止天
*/
function getAddTimestamp(min = 0, max = 1) {
let now = timeUtil.timestamp2Time(timeUtil.time(), 'Y-M-D'); //转为当天0点
now = timeUtil.time2Timestamp(now);
return now + getIntBetween(min * 86400 * 1000, max * 86400 * 1000);
}
/** 生日 */
function getDate(start = 1900, end = 2020) {
start = start + '-01-01 00:00:00';
start = timeUtil.time2Timestamp(start);
end = end + '-12-31 23:59:59';
end = timeUtil.time2Timestamp(end);
let time = getIntBetween(start, end);
return timeUtil.timestamp2Time(time, 'Y-M-D');
}
/** 整数 */
function getInt(size) {
let t = '';
for (var i = 0; i < size; i++) {
t += Math.floor(Math.random() * 10);
}
return t;
}
/** 随机数组 */
function getRdArr(arr) {
return getRnd(arr);
}
/** 随机数 */
function getIntBetween(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
/** 随机字符串 */
function getStr(size) {
let text = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let rdmIndex = text => Math.random() * text.length | 0;
let rdmString = '';
for (; rdmString.length < size; rdmString += text.charAt(rdmIndex(text)));
return rdmString;
}
/** 随机数字字符串 */
function getIntStr(size) {
let text = '0123456789';
let rdmIndex = text => Math.random() * text.length | 0;
let rdmString = '';
for (; rdmString.length < size; rdmString += text.charAt(rdmIndex(text)));
return String(rdmString);
}
/** 随机字符串小写 */
function getStrLower(size) {
return getStr(size).toLowerCase();
}
/** 随机字符串大写 */
function getStrUpper(size) {
return getStr(size).toUpperCase();
}
function getUuid() {
let s = [];
let hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
let uuid = s.join("");
return uuid;
}
/** 学院 */
function getCollege(isNullable = false, ex = '') {
let data = '地球科学学院,环境科学与工程学院,化学与生物工程学院,材料科学与工程学院,土木与建筑工程学院,测绘地理信息学院,信息科学与工程学院,机械与控制工程学院,珠宝学院,马克思主义学院,公共管理与传媒学院,商学院,旅游与风景园林学院,艺术学院,外国语学院,理学院,文学与新闻传播学院,外国语学院,建筑与艺术学院,商学院,法学院,马克思主义学院,公共管理学院,数学与统计学院,物理与电子学院,化学化工学院,文学系,法学系,哲学系,医学系,力学系,理学系,数学系,物理系,化学系,计算机系,自动化系,口腔医学系,英语系,外语系,法语系,德语系,日语系,西班牙语系';
return getRnd(data, isNullable, ex);
}
/** 专业 */
function getItem(isNullable = false, ex = '') {
let data = '音乐表演,音乐学,作曲与作曲技术理论,舞蹈表演,舞蹈学,舞蹈编导,舞蹈教育,航空服务艺术与管理,流行音乐,音乐治疗,流行舞蹈,表演,戏剧学,电影学,戏剧影视文学,广播电视编导,戏剧影视导演,戏剧影视美术设计,录音艺术,播音与主持艺术,动画,美术学,绘画,雕塑,摄影,书法学,中国画,实验艺术,跨媒体艺术,文物保护与修复,漫画,艺术设计学,视觉传达设计,环境设计,产品设计,服装与服饰设计,公共艺术,工艺美术,数字媒体艺术,艺术与科技,陶瓷艺术设计,新媒体设计,包装设计,教育学,科学教育,人文教育,教育技术学,艺术教育,学前教育,小学教育,特殊教育,华文教育,教育康复学,卫生教育,法学,知识产权,监狱学,信用风险管理与法律防控,国际经贸规则,司法警察学,社区矫正,工商管理,市场营销,会计学,财务管理,国际商务,人力资源管理,审计学,资产评估,物业管理,文化产业管理,劳动关系,体育经济与管理,财务会计教育,市场营销教育,零售业管理,农林经济管理,农村区域发展 ,公共事业管理,行政管理,劳动与社会保障,土地资源管理,城市管理,海关管理,交通管理,海事管理,公共关系学,健康服务与管理,海警后勤管理,数学与应用数学,信息与计算科学,数理基础科学,数据计算及应用 ,物理学,应用物理学,核物理,声学,系统科学与工程,地理科学,自然地理与资源环境,人文地理与城乡规划,地理信息科学 ,机械设计制造及其自动化,材料成型及控制工程,机械电子工程,工业设计,过程装备与控制工程,车辆工程,汽车服务工程,机械工艺技术,微机电系统工程,机电技术教育,汽车维修工程教育,智能制造工程,材料科学与工程材料物理,材料化学,冶金工程,金属材料工程,无机非金属材料工程,高分子材料与工程,复合材料与工程,粉体材料科学与工程,宝石及材料工艺学,焊接技术与工程,功能材料,纳米材料与技术,新能源材料与器件,材料设计科学与工程,光电信息科学与工程,信息工程,广播电视工程,水声工程,电子封装技术,集成电路设计与集成系统,医学信息工程,电磁场与无线技术,电波传播与天线,电子信息科学与技术,电信工程及管理,应用电子技术教育,数字媒体技术,智能科学与技术,空间信息与数字技术,电子与计算机工程,数据科学与大数据技术,网络空间安全,新媒体技术,电影制作,保密技术,服务科学与工程,虚拟现实技术,区块链工程,建筑环境与能源应用工程,给排水科学与工程,建筑电气与智能化,城市地下空间工程,道路桥梁与渡河工程,铁道工程,智能建造,土木、水利与海洋工程,土木、水利与交通工程,采矿工程,石油工程,矿物加工工程,油气储运工程,矿物资源工程,海洋油气工程 ,纺织工程,服装设计与工程,非织造材料与工程,服装设计与工艺教育,丝绸设计与工程';
return getRnd(data, isNullable, ex);
}
/** 行业 */
function getTrade(isNullable = false, ex = '') {
let data = ['经营', '销售', '市场营销', '公关', '客户服务', '人力资源', '行政HR', '财务/审计/统计', '文职', '翻译', '计算机/IT', '电子/通讯', '设计', '工业/工厂', '金融/经济', '法律', '机械', '技工', '房地产/土建', '咨询/顾问', '医疗/护理/保健', '服务业', '政府机关', '事业单位', '学生/研究生', '化工', '冶金/地质'];
return getRnd(data, isNullable, ex);
}
/** 学历 */
function getEdu(isNullable = false, ex = '') {
let data = '中学,高职,大专,本科,硕士,博士,博士后,其他';
return getRnd(data, isNullable, ex);
}
/** 职位 */
function getDuty(isNullable = false, ex = '') {
let data = 'CTO,CEO,CFO,研发,销售,采购,董事长,老板,自由自由者,中层领导,部门经理,大区经理';
return getRnd(data, isNullable, ex);
}
/** 资源 */
function getResource(isNullable = false, ex = '') {
let data = '法律咨询,管理咨询,企业辅导,上市辅导,创业交流,投资融资,医疗咨询,教育交流,开发技术交流,研发交流,未来探讨,大宗商品,销售网络共享,艺术品鉴赏,供应链共享,进修交流,财会督导,审计辅导,企业治理,工程监理,硬件生产,小商品生产,电商,二类电商,早教,公考,艺术设计,人力资源,地质勘探,招工招聘,游戏开发,销售采购,市场营销,电子通讯,经济探讨,机械制造,产业经理,轻工业,化工化学,海外电商,企业出海,翻译,心理咨询,餐饮酒店,民宿,旅游自驾,服务业,租车,自媒体新媒体行业,文职人员,军迷,学习共勉,体育活动,打球约饭,户外旅行,文艺青年,小镇青年,斜杠青年,交通运输,民航机票,系统集成,售前服务,维修';
return getRnd(data, isNullable, ex);
}
/** 自我介绍 */
function getMotto(isNullable = false, ex = '') {
let data = '生无一锥土,常有四海心 ,志在山顶的人,不会贪念山腰的风景 ,人之所以能,是相信能 ,卒子过河,意在吃帅 ,心志要坚,意趣要乐 ,贫困教会贫困者一切 ,欲望以提升热忱,毅力以磨平高山 ,人生不得行胸怀,虽寿百岁犹为无也 ,人之所以异于禽者,唯志而已矣!,每一发奋努力的背后,必有加倍的赏赐 ,治天下者必先立其志 ,以天下为己任 ,一人立志,万夫莫敌 ,志高山峰矮,路从脚下伸 ,鹰爱高飞,鸦栖一枝 ,莫为一身之谋,而有天下之志 ,人之所以能,是相信能,励志短语,没有天生的信心,只有不断培养的信心 ,世上没有绝望的处境,只有对处境绝望的人 ,人格的完善是本,财富的确立是末 ,在年轻人的颈项上,没有什么东西能比事业心这颗灿烂的宝珠 ,壮志与毅力是事业的双翼 ,心有多大,舞台就有多大 ,志正则众邪不生 ,母鸡的理想不过是一把糠 ,死犹未肯输心去,贫亦其能奈我何!,鸟贵有翼,人贵有志 ,有志登山顶,无志站山脚 ,没有一种不通过蔑视忍受和奋斗就可以征服的命运 ,远大的希望造就伟大的人物 ,志不立,天下无可成之事 ,有志者能使石头长出青草来 ,莫找借口失败,只找理由成功 ,男子千年志,吾生未有涯 ,鱼跳龙门往上游 ,有志者,事竟成 ,强行者有志 ,心随朗月高,志与秋霜洁 ,与其当一辈子乌鸦,莫如当一次鹰 ,石看纹理山看脉,人看志气树看材 ,志当存高远 ,任何的限制,都是从自己的内心开始的 ,志,气之帅也 ,一个人如果胸无大志,既使再有壮丽的举动也称不上是伟人 ,立志是事业的大门,工作是登门入室的旅程 ,志气和贫困是患难兄弟,世人常见他们伴在一起 ,失败是成功之母 ,对的,坚持;错的,放弃!,丈夫志不大,何以佐乾坤 ,鸭仔无娘也长大,几多白手也成家 ,我走得很慢,但是我从来不会后退,面对太阳,阴影将落在你的背后,困境之中,饱含机遇,执着于理想,纯粹于当下,不要轻言放弃,否则对不起自己,含泪播种的人一定能含笑收获,日益努力,而后风生水起,若要梦想实现,先从梦中醒来,今天比昨天好,就是希望,希望叫醒你的不是闹钟而是理想,坚定信念的人都是英雄,欲戴皇冠,必承其重,昨日之深渊,来日之浅谈,天越黑,星星越亮,岂能尽如人意,但求无愧我心,世上没什么运气,只有努力去挑战,日出之美便在于它脱胎于最深的黑暗,不要等待机会,而要创造机会,成功的秘诀在于对目标的执着追求,我把苦难挫折当作自己生存的最好导师,黑夜无论怎样悠长,白昼总会到来,海到无边天作岸,山登绝顶我为峰,除了放弃尝试以外没有失败,有梦就别怕痛,想赢就别喊停, 与其羡慕别人,不如自己努力,努力就能成功,坚持确保胜利,永不言败,是成功者的最佳品格,人生没有彩排,每天都是现场直播,火把倒下,火焰依然向上,低头哭过别忘了抬头继续走,有种脾气叫不放弃,风乍起,合当奋意向人生,莫问收获,但问耕耘,即使身在生活,也要做你理想的卧底,我只身前行,却仿佛带着一万雄兵,熬过一切,星光璀璨,没有人帮你,说明你一个人可以,让理想生活的样子清晰可见,趁我们头脑发热,我们要不顾一切,念念不忘,必有回响,一生很短,你要大胆,容易走的路,一般都很拥挤,那些杀不死我们的,终将让我们更强大,你利用时间的方式,就是塑造自己的方式,每一个不曾起舞的日子,都是对生命的辜负,猛兽总是独行,牛羊才成群结队,你迷茫的原因在于读书太少而想的太多,对未来真正的慷慨,是把一切献给现在,没有一点儿疯狂,生活就不值得过,生活在阴沟里,但仍有人仰望星空,怕输的人已经输了,不要忘记人生是要战斗到死, 抱怨身处黑暗,不如提灯前行,患难困苦,是磨炼人格之高等学校,失败不是悲剧,放弃才是,画工须画云中龙,为人须为人中雄,博观而约取,厚积而薄发,志在山顶的人,不会贪恋山腰的风景,别为失败找理由,要为成功找方法,迷失的时候,选择更艰辛的那条路,命是弱者的借口,运是强者的谦词,如果今天不走的话,明天就要跑,今天度过的一天明天就找不回来了,生活绝不会因为你胆小怯懦而饶过你,最可怕的敌人,就是没有坚强的信念,梦想一旦被付诸行动,就会变得神圣,寄言燕雀莫相唣,自有云霄万里高,人若有志,万事可为,志不可一日坠,人不可一日放,苦难,是化了妆的祝福,没有实力的愤怒毫无意义,在避风的港湾里,找不到昂扬的帆,大胆的尝试只等于成功了一半,天才就是无止境刻苦勤奋的能力,你是自己人生的设计师,苦想没盼头,苦干有奔头,世界会向那些有目标和远见的人让路,挫折其实就是迈向成功所应缴的学费,欲望以提升热忱,毅力以磨平高山,用行动祈祷比用言语更能够使上帝了解,志不立,天下无可成之事,志向和热爱是伟大行为的双翼,水激石则鸣,人激志则宏,雄心壮志是茫茫黑夜中的北斗星,贫而懒惰乃真穷,贱而无志乃真贱,目标越接近,困难越增加,绳锯木断,水滴石穿,男儿不展风云志,空负天生八尺躯,天才就是无止境刻苦勤奋的能力,苦难是人生的老师,成功的秘诀,在永不改变既定的目的,平凡的脚步也可以走完伟大的行程,如果你有梦想的话,就要去捍卫它,永远要面对眼前的这些困境,如果我放弃,就是向那些错看我的人屈服,运气,就是机会碰巧撞到了你的努力,哪有什么胜利可言,挺住就意味着一切,过去属于死神,未来属于你自己,失败是坚忍的最后考验,流水在碰到底处时才会释放活力';
return getRnd(data, isNullable, ex);
}
/** 用户头像 */
function getAvatar(isNullable) {
if (getNullable(isNullable)) return '';
return 'https://7265-release-7g51ulsq6451a0a6-1304820041.tcb.qcloud.la/mini/user_pic/' + getIntBetween(1, 200) + '.jpg';
}
/** 是否为空 */
function getNullable(isNullable) {
if (!isNullable) return false;
let rd = getIntBetween(0, 1);
if (rd % 2 == 0)
return true;
else
return false;
}
module.exports = {
getUuid,
getRnd,
getIdCard,
getProvince,
getProvinceAbbr,
getCity,
getArea,
getCountry,
getStreet,
getAddress,
getCompany,
getCompanyPrefix,
getResource,
getMotto,
getContent,
getWord,
getWeek,
getMonth,
getTimestamp,
getAddTimestamp,
getFirstName,
getFemaleName,
getMaleName,
getName,
getInt,
getRdArr,
getIntBetween,
getIntStr,
getStr,
getStrLower,
getStrUpper,
getMobile,
getPhone,
getEnWord,
getEmail,
getDomain,
getDate,
getItem,
getCollege,
getTrade,
getEdu,
getDuty,
getAvatar
}
================================================
FILE: cloudfunctions/mcloud/framework/lib/md5_lib.js
================================================
/**
* Notes: MD5类库
* Ver : CCMiniCloud Framework 2.15.1 ALL RIGHTS RESERVED BY cClinux0730 (wechat)
* Date: 2021-03-01 14:00:00
*/
function safe_add(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF)
var msw = (x >> 16) + (y >> 16) + (lsw >> 16)
return (msw << 16) | (lsw & 0xFFFF)
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function rol(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt))
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function cmn(q, a, b, x, s, t) {
return safe_add(rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b)
}
function ff(a, b, c, d, x, s, t) {
return cmn((b & c) | ((~b) & d), a, b, x, s, t)
}
function gg(a, b, c, d, x, s, t) {
return cmn((b & d) | (c & (~d)), a, b, x, s, t)
}
function hh(a, b, c, d, x, s, t) {
return cmn(b ^ c ^ d, a, b, x, s, t)
}
function ii(a, b, c, d, x, s, t) {
return cmn(c ^ (b | (~d)), a, b, x, s, t)
}
/*
* Calculate the MD5 of an array of little-endian words, producing an array
* of little-endian words.
*/
function coreMD5(x) {
var a = 1732584193
var b = -271733879
var c = -1732584194
var d = 271733878
for (var i = 0; i < x.length; i += 16) {
var olda = a
var oldb = b
var oldc = c
var oldd = d
a = ff(a, b, c, d, x[i + 0], 7, -680876936)
d = ff(d, a, b, c, x[i + 1], 12, -389564586)
c = ff(c, d, a, b, x[i + 2], 17, 606105819)
b = ff(b, c, d, a, x[i + 3], 22, -1044525330)
a = ff(a, b, c, d, x[i + 4], 7, -176418897)
d = ff(d, a, b, c, x[i + 5], 12, 1200080426)
c = ff(c, d, a, b, x[i + 6], 17, -1473231341)
b = ff(b, c, d, a, x[i + 7], 22, -45705983)
a = ff(a, b, c, d, x[i + 8], 7, 1770035416)
d = ff(d, a, b, c, x[i + 9], 12, -1958414417)
c = ff(c, d, a, b, x[i + 10], 17, -42063)
b = ff(b, c, d, a, x[i + 11], 22, -1990404162)
a = ff(a, b, c, d, x[i + 12], 7, 1804603682)
d = ff(d, a, b, c, x[i + 13], 12, -40341101)
c = ff(c, d, a, b, x[i + 14], 17, -1502002290)
b = ff(b, c, d, a, x[i + 15], 22, 1236535329)
a = gg(a, b, c, d, x[i + 1], 5, -165796510)
d = gg(d, a, b, c, x[i + 6], 9, -1069501632)
c = gg(c, d, a, b, x[i + 11], 14, 643717713)
b = gg(b, c, d, a, x[i + 0], 20, -373897302)
a = gg(a, b, c, d, x[i + 5], 5, -701558691)
d = gg(d, a, b, c, x[i + 10], 9, 38016083)
c = gg(c, d, a, b, x[i + 15], 14, -660478335)
b = gg(b, c, d, a, x[i + 4], 20, -405537848)
a = gg(a, b, c, d, x[i + 9], 5, 568446438)
d = gg(d, a, b, c, x[i + 14], 9, -1019803690)
c = gg(c, d, a, b, x[i + 3], 14, -187363961)
b = gg(b, c, d, a, x[i + 8], 20, 1163531501)
a = gg(a, b, c, d, x[i + 13], 5, -1444681467)
d = gg(d, a, b, c, x[i + 2], 9, -51403784)
c = gg(c, d, a, b, x[i + 7], 14, 1735328473)
b = gg(b, c, d, a, x[i + 12], 20, -1926607734)
a = hh(a, b, c, d, x[i + 5], 4, -378558)
d = hh(d, a, b, c, x[i + 8], 11, -2022574463)
c = hh(c, d, a, b, x[i + 11], 16, 1839030562)
b = hh(b, c, d, a, x[i + 14], 23, -35309556)
a = hh(a, b, c, d, x[i + 1], 4, -1530992060)
d = hh(d, a, b, c, x[i + 4], 11, 1272893353)
c = hh(c, d, a, b, x[i + 7], 16, -155497632)
b = hh(b, c, d, a, x[i + 10], 23, -1094730640)
a = hh(a, b, c, d, x[i + 13], 4, 681279174)
d = hh(d, a, b, c, x[i + 0], 11, -358537222)
c = hh(c, d, a, b, x[i + 3], 16, -722521979)
b = hh(b, c, d, a, x[i + 6], 23, 76029189)
a = hh(a, b, c, d, x[i + 9], 4, -640364487)
d = hh(d, a, b, c, x[i + 12], 11, -421815835)
c = hh(c, d, a, b, x[i + 15], 16, 530742520)
b = hh(b, c, d, a, x[i + 2], 23, -995338651)
a = ii(a, b, c, d, x[i + 0], 6, -198630844)
d = ii(d, a, b, c, x[i + 7], 10, 1126891415)
c = ii(c, d, a, b, x[i + 14], 15, -1416354905)
b = ii(b, c, d, a, x[i + 5], 21, -57434055)
a = ii(a, b, c, d, x[i + 12], 6, 1700485571)
d = ii(d, a, b, c, x[i + 3], 10, -1894986606)
c = ii(c, d, a, b, x[i + 10], 15, -1051523)
b = ii(b, c, d, a, x[i + 1], 21, -2054922799)
a = ii(a, b, c, d, x[i + 8], 6, 1873313359)
d = ii(d, a, b, c, x[i + 15], 10, -30611744)
c = ii(c, d, a, b, x[i + 6], 15, -1560198380)
b = ii(b, c, d, a, x[i + 13], 21, 1309151649)
a = ii(a, b, c, d, x[i + 4], 6, -145523070)
d = ii(d, a, b, c, x[i + 11], 10, -1120210379)
c = ii(c, d, a, b, x[i + 2], 15, 718787259)
b = ii(b, c, d, a, x[i + 9], 21, -343485551)
a = safe_add(a, olda)
b = safe_add(b, oldb)
c = safe_add(c, oldc)
d = safe_add(d, oldd)
}
return [a, b, c, d]
}
/*
* Convert an array of little-endian words to a hex string.
*/
function binl2hex(binarray) {
var hex_tab = "0123456789abcdef"
var str = ""
for (var i = 0; i < binarray.length * 4; i++) {
str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) +
hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF)
}
return str
}
/*
* Convert an array of little-endian words to a base64 encoded string.
*/
function binl2b64(binarray) {
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var str = ""
for (var i = 0; i < binarray.length * 32; i += 6) {
str += tab.charAt(((binarray[i >> 5] << (i % 32)) & 0x3F) |
((binarray[i >> 5 + 1] >> (32 - i % 32)) & 0x3F))
}
return str
}
/*
* Convert an 8-bit character string to a sequence of 16-word blocks, stored
* as an array, and append appropriate padding for MD4/5 calculation.
* If any of the characters are >255, the high byte is silently ignored.
*/
function str2binl(str) {
var nblk = ((str.length + 8) >> 6) + 1 // number of 16-word blocks
var blks = new Array(nblk * 16)
for (var i = 0; i < nblk * 16; i++) blks[i] = 0
for (var i = 0; i < str.length; i++)
blks[i >> 2] |= (str.charCodeAt(i) & 0xFF) << ((i % 4) * 8)
blks[i >> 2] |= 0x80 << ((i % 4) * 8)
blks[nblk * 16 - 2] = str.length * 8
return blks
}
/*
* Convert a wide-character string to a sequence of 16-word blocks, stored as
* an array, and append appropriate padding for MD4/5 calculation.
*/
function strw2binl(str) {
var nblk = ((str.length + 4) >> 5) + 1 // number of 16-word blocks
var blks = new Array(nblk * 16)
for (var i = 0; i < nblk * 16; i++) blks[i] = 0
for (var i = 0; i < str.length; i++)
blks[i >> 1] |= str.charCodeAt(i) << ((i % 2) * 16)
blks[i >> 1] |= 0x80 << ((i % 2) * 16)
blks[nblk * 16 - 2] = str.length * 16
return blks
}
/*
* External interface
*/
function hexMD5(str) {
return binl2hex(coreMD5(str2binl(str)))
}
function hexMD5w(str) {
return binl2hex(coreMD5(strw2binl(str)))
}
function b64MD5(str) {
return binl2b64(coreMD5(str2binl(str)))
}
function b64MD5w(str) {
return binl2b64(coreMD5(strw2binl(str)))
}
/* Backward compatibility */
function calcMD5(str) {
return binl2hex(coreMD5(str2binl(str)))
}
module.exports = {
md5: hexMD5
}
================================================
FILE: cloudfunctions/mcloud/framework/lib/mini_lib.js
================================================
/**
* Notes: 小程序封装类库
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cClinux0730 (wechat)
* Date: 2020-09-06 14:00:00
*/
const cloudBase = require('../cloud/cloud_base.js');
const cloudUtil = require('../cloud/cloud_util.js');
const config = require('../../config/config');
// 消息长度截取
function fmtThing(str) { //20个以内字符,可汉字、数字、字母或符号组合
return str.substr(0, 20);
}
function fmtCharacterString(str) { //32位以内数字、字母或符号
return str.substr(0, 32);
}
function fmtPhrase(str) { //5个以内汉字
return str.substr(0, 5);
}
/**
* 发送一次性消息
* @param {*} body
* @param {*} key
*/
async function sendMiniOnceTempMsg(body, key = '') {
// console.log('##sendOnceTempMsg[' + key + ']', body);
let cloud = cloudBase.getCloud();
try {
// 默认参数
body.lang = 'zh_CN';
body.miniprogramState = 'formal';
await cloud.openapi.subscribeMessage.send(body);
} catch (err) {
cloudUtil.log('##sendOnceTempMsg[' + key + ']', err);
}
}
module.exports = {
sendMiniOnceTempMsg,
fmtThing,
fmtCharacterString,
fmtPhrase
}
================================================
FILE: cloudfunctions/mcloud/framework/platform/controller/base_admin_controller.js
================================================
/**
* Notes: 后台管理控制器模块
* Ver : CCMiniCloud Framework 2.0.3 ALL RIGHTS RESERVED BY cclinuX0730 (wechat)
* Date: 2022-05-26 19:20:00
*/
const BaseController = require('./base_controller.js');
const BaseAdminService = require('../service/base_admin_service.js');
const LogModel = require('../model/log_model.js');
const timeUtil = require('../../../framework/utils/time_util.js');
class BaseAdminController extends BaseController {
constructor(route, openId, event) {
super(route, openId, event);
// 当前时间戳
this._timestamp = timeUtil.time();
this._admin = null;
this._adminId = '0';
}
/** 是否管理员 */
async isAdmin() {
// 判断是否管理员
let service = new BaseAdminService();
let admin = await service.isAdmin(this._token);
this._admin = admin;
this._adminId = admin._id;
}
/** 是否超级管理员 */
async isSuperAdmin() {
// 判断是否管理员
let service = new BaseAdminService();
let admin = await service.isSuperAdmin(this._token);
this._admin = admin;
this._adminId = admin._id;
}
/** 记录日志 */
async log(content, type) {
let service = new BaseAdminService();
await service.insertLog(content, this._admin, type);
}
async logSys(content) {
await this.log(content, LogModel.TYPE.SYS);
}
async logUser(content) {
await this.log(content, LogModel.TYPE.USER);
}
async logOther(content) {
await this.log(content, LogModel.TYPE.OTHER);
}
async logNews(content) {
await this.log(content, LogModel.TYPE.NEWS);
}
}
module.exports = BaseAdminController;
================================================
FILE: cloudfunctions/mcloud/framework/platform/controller/base_controller.js
================================================
/**
* Notes: 基础控制器
* Ver : CCMiniCloud Framework 2.0.4 ALL RIGHTS RESERVED BY cclinUx0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const config = require('../../../config/config.js');
const timeUtil = require('../../utils/time_util.js');
const util = require('../../utils/util.js');
const dataCheck = require('../../validate/data_check.js');
const AppError = require('../../core/app_error.js');
const appCode = require('../../core/app_code.js');
class BaseController {
constructor(route, openId, event) {
this._route = route; // 路由
this._openId = openId; //用户身份
this._event = event; // 所有参数
this._request = event.params; //数据参数
if (!openId) {
console.error('OPENID is unfined');
throw new AppError('OPENID is unfined', appCode.SVR);
}
let userId = openId;
this._token = event.token || '';
this._userId = userId;
// 当前时间戳
this._timestamp = timeUtil.time();
let time = timeUtil.time('Y-M-D h:m:s');
console.log('------------------------');
console.log(`【${time}】【Request -- ↘↘↘】\n【↘Token = ${this._token}】\n【↘USER-ID = ${userId}】\n【↘↘IN DATA】=\n`, JSON.stringify(this._request, null, 4));
}
/**
* 数据校验
* @param {*} rules
*/
validateData(rules = {}) {
let input = this._request;
return dataCheck.check(input, rules);
}
// 取得某个具体的参数值
getParameter(name) {
let input = this._request;
if (util.isDefined(input[name]))
return input[name];
else
return '';
}
}
module.exports = BaseController;
================================================
FILE: cloudfunctions/mcloud/framework/platform/model/admin_model.js
================================================
/**
* Notes: 系统管理员实体
* Date: 2021-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.0.5 ALL RIGHTS RESERVED BY CCLINUX0730 (wechat)
*/
const BaseModel = require('./base_model.js');
class AdminModel extends BaseModel {
}
// 集合名
AdminModel.CL = BaseModel.C('admin');
AdminModel.DB_STRUCTURE = {
_pid: 'string|true',
ADMIN_ID: 'string|true',
ADMIN_NAME: 'string|true',
ADMIN_DESC: 'string|true',
ADMIN_PHONE: 'string|false|comment=手机',
ADMIN_PASSWORD: 'string|true|comment=密码',
ADMIN_STATUS: 'int|true|default=1|comment=状态:0=禁用 1=启用',
ADMIN_LOGIN_CNT: 'int|true|default=0|comment=登录次数',
ADMIN_LOGIN_TIME: 'int|true|default=0|comment=最后登录时间',
ADMIN_TYPE: 'int|true|default=0|comment=类型 0=普通管理员 1=超级管理员',
ADMIN_TOKEN: 'string|false|comment=当前登录token',
ADMIN_TOKEN_TIME: 'int|true|default=0|comment=当前登录token time',
ADMIN_ADD_TIME: 'int|true',
ADMIN_EDIT_TIME: 'int|true',
ADMIN_ADD_IP: 'string|false',
ADMIN_EDIT_IP: 'string|false',
};
// 字段前缀
AdminModel.FIELD_PREFIX = "ADMIN_";
module.exports = AdminModel;
================================================
FILE: cloudfunctions/mcloud/framework/platform/model/base_model.js
================================================
/**
* Notes: 实体基类
* Date: 2021-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.0.6 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const MultiModel = require('../../../framework/database/multi_model.js');
class BaseModel extends MultiModel {
}
module.exports = BaseModel;
================================================
FILE: cloudfunctions/mcloud/framework/platform/model/log_model.js
================================================
/**
* Notes: 后台操作日志实体
* Ver : CCMiniCloud Framework 2.0.7 ALL RIGHTS RESERVED BY cclinuX0730 (wechat)
* Date: 2020-10-16 19:20:00
*/
const BaseModel = require('./base_model.js');
class LogModel extends BaseModel {
}
// 集合名
LogModel.CL = BaseModel.C('log');
LogModel.DB_STRUCTURE = {
_pid: 'string|true',
LOG_ID: 'string|true',
LOG_ADMIN_ID: 'string|true|comment=管理员',
LOG_ADMIN_DESC: 'string|false',
LOG_ADMIN_NAME: 'string|true',
LOG_CONTENT: 'string|true',
LOG_TYPE: 'int|true|comment=日志类型 ',
LOG_ADD_TIME: 'int|true',
LOG_EDIT_TIME: 'int|true',
LOG_ADD_IP: 'string|false',
LOG_EDIT_IP: 'string|false',
};
// 字段前缀
LogModel.FIELD_PREFIX = "LOG_";
LogModel.TYPE = {
SYS: 0,
USER: 1,
NEWS: 2,
OTHER: 99,
}
LogModel.TYPE_DESC = {
SYS: '系统',
USER: '用户',
NEWS: '文章',
OTHER: '其他',
}
module.exports = LogModel;
================================================
FILE: cloudfunctions/mcloud/framework/platform/service/base_admin_service.js
================================================
/**
* Notes: 后台管理模块业务基类
* Date: 2021-03-15 07:48:00
* Ver : CCMiniCloud Framework 2.0.8 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseService = require('./base_service.js');
const timeUtil = require('../../../framework/utils/time_util.js');
const appCode = require('../../../framework/core/app_code.js');
const config = require('../../../config/config.js');
const AdminModel = require('../model/admin_model.js');
const LogModel = require('../model/log_model.js');
class BaseAdminService extends BaseService {
/** 是否管理员 */
async isAdmin(token) {
if (config.IS_DEMO) { // 演示版本
let admin = {};
admin.ADMIN_NAME = 'demo-admin';
admin.ADMIN_DESC = '体验用户';
admin.ADMIN_ID = '1';
admin.ADMIN_PHONE = '13900000000';
admin.ADMIN_LOGIN_CNT = 0;
admin.ADMIN_LOGIN_TIME = '';
admin.ADMIN_TYPE = 0;
admin.ADMIN_STATUS = 1;
return admin;
}
let where = {
ADMIN_TOKEN: token,
ADMIN_TOKEN_TIME: ['>', timeUtil.time() - config.ADMIN_LOGIN_EXPIRE * 1000], // token有效时间
ADMIN_STATUS: 1,
}
let admin = await AdminModel.getOne(where, 'ADMIN_ID,ADMIN_PHONE,ADMIN_NAME,ADMIN_TYPE,ADMIN_DESC');
if (!admin)
this.AppError('管理员不存在', appCode.ADMIN_ERROR);
return admin;
}
/** 是否超级管理员 */
async isSuperAdmin(token) {
let where = {
ADMIN_TOKEN: token,
ADMIN_TOKEN_TIME: ['>', timeUtil.time() - config.ADMIN_LOGIN_EXPIRE * 1000], // token有效时间
ADMIN_STATUS: 1,
ADMIN_TYPE: 1
}
let admin = await AdminModel.getOne(where, 'ADMIN_ID,ADMIN_PHONE,ADMIN_NAME,ADMIN_TYPE');
if (!admin)
this.AppError('超级管理员不存在', appCode.ADMIN_ERROR);
return admin;
}
/** 写入日志 */
async insertLog(content, admin, type) {
if (!admin) return;
let data = {
LOG_CONTENT: content,
LOG_ADMIN_ID: admin._id,
LOG_ADMIN_NAME: admin.ADMIN_NAME,
LOG_ADMIN_DESC: admin.ADMIN_DESC,
LOG_TYPE: type
}
await LogModel.insert(data);
}
}
module.exports = BaseAdminService;
================================================
FILE: cloudfunctions/mcloud/framework/platform/service/base_service.js
================================================
/**
* Notes: 基础业务逻辑
* Ver : CCMiniCloud Framework 2.0.9 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-04-24 04:00:00
*/
const AppError = require('../../core/app_error.js');
const appCode = require('../../core/app_code.js');
const timeUtil = require('../../utils/time_util.js');
class BaseService {
constructor() {
// 当前时间戳
this._timestamp = timeUtil.time();
}
/**
* 抛出异常
* @param {*} msg
* @param {*} code
*/
AppError(msg, code = appCode.LOGIC) {
throw new AppError(msg, code);
}
/** 时期范围处理 */
fmtSearchDate(where, search, field) {
if (!search || search.length != 21 || !search.includes('#')) return where;
let arr = search.split('#');
let start = arr[0];
let end = arr[1];
where[field] = ['between', start, end];
return where;
}
/* 数据库字段排序处理 */
fmtOrderBySort(sortVal, defaultSort) {
let orderBy = {
[defaultSort]: 'desc'
};
if (sortVal.includes('|')) {
let field = sortVal.split('|')[0];
let order = sortVal.split('|')[1];
orderBy = {
[field]: order,
};
if (defaultSort != field) orderBy[defaultSort] = 'desc';
}
return orderBy;
}
}
module.exports = BaseService;
================================================
FILE: cloudfunctions/mcloud/framework/utils/constant.js
================================================
/**
* Notes: 通用常量定义
* Ver : CCMiniCloud Framework 2.32.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
module.exports = {
}
================================================
FILE: cloudfunctions/mcloud/framework/utils/data_util.js
================================================
/**
* Notes: 字符相关操作函数
* Ver : CCMiniCloud Framework 2.33.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const timeUtil = require('./time_util.js');
/**
* 生成一个特定范围内的随机数
*/
const genRandomNum = (min, max) => (Math.random() * (max - min + 1) | 0) + min;
// 生成一个随机的数字字母字符串
const genRandomString = len => {
const text = 'abcdefghijklmnopqrstuvwxyz0123456789';
const rdmIndex = text => Math.random() * text.length | 0;
let rdmString = '';
for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
return rdmString;
}
// 生成一个随机的数字字符串
const genRandomIntString = len => {
const text = '0123456789';
const rdmIndex = text => Math.random() * text.length | 0;
let rdmString = '';
for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
return rdmString;
}
// 生成一个随机的字母字符串
const genRandomAlpha = len => {
const text = 'abcdefghijklmnopqrstuvwxyz';
const rdmIndex = text => Math.random() * text.length | 0;
let rdmString = '';
for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
return rdmString;
}
// 根据数据库自定义表单提取导出表格标题
function getTitleByForm(arr) {
let formTitle = [];
for (let k = 0; k < arr.length; k++) {
if (arr.type == 'image' || arr.type == 'content') continue;
formTitle.push({
column: arr[k].title,
wch: 30
});
}
return formTitle;
}
// 根据数据库自定义表单提取数据
function getValByForm(arr, mark, title) {
for (let k = 0; k < arr.length; k++) {
if (arr[k].mark == mark || arr[k].title == title) {
if (arr[k].type == 'image') return '图片';
if (arr[k].type == 'content') return '图文内容';
if (arr[k].type == 'switch') {
if (arr[k].val === true)
return '是';
else
return '否';
}
return arr[k].val;
}
}
return '';
}
// 数据库自定义表单forms值修正
function dbFormsFix(forms) {
for (let k = 0; k < forms.length; k++) {
if (forms[k].type == 'number' || forms[k].type == 'digit') {
forms[k].val = Number(forms[k].val);
if (isNaN(forms[k].val)) forms[k].val = null;
}
}
return forms;
}
// 数据库自定义表单forms转为obj
function dbForms2Obj(forms, excludeContent = false) {
forms = dbFormsFix(forms); //数据类型修正
if (forms.length == 0) return { 'no': 'none' };
let obj = {};
for (let k = 0; k < forms.length; k++) {
if (excludeContent && forms[k].type == 'content') continue;
obj[forms[k].mark] = forms[k].val;
}
return obj;
}
// 构造当前ID
function makeID() {
let id = timeUtil.time('YMDhms') + ''; //秒
//毫秒3位
let miss = timeUtil.time() % 1000 + '';
if (miss.length == 0)
miss = '000';
else if (miss.length == 1)
miss = '00' + miss;
else if (miss.length == 2)
miss = '0' + miss;
return id + miss;
}
// 拆分一维数组为二维数组
function spArr(arr, size) {
if (!arr || !Array.isArray(arr) || arr.length == 0) return arr;
let newArray = [];
let index = 0;
while (index < arr.length) {
newArray.push(arr.slice(index, index += size));
}
return newArray;
}
/**
* 把字符串格式化为数组
* @param {*} str
* @param {*} sp
*/
function str2Arr(str, sp = ',') {
if (str && Array.isArray(str)) return str;
str = str.replace(/,/g, sp);
let arr = str.split(sp);
for (let i = 0; i < arr.length; i++) {
arr[i] = arr[i].trim();
if (isNumber(arr[i])) {
arr[i] = Number(arr[i]);
}
}
return arr;
}
/**
* 校验只要是数字(包含正负整数,0以及正负浮点数)就返回true
* @param {*} val
* @returns bool
*/
function isNumber(val) {
var reg = /^[0-9]+.?[0-9]*$/;
if (reg.test(val)) {
return true;
} else {
return false;
}
}
/**
* 提取对象数组的某个属性数组,如[{'x':1},{'x':2}] 提取 x得到[1,2]
* @param {*} arr
* @param {*} key
* @returns []
*/
function getArrByKey(arr, key) {
if (!Array.isArray(arr)) return;
return arr.map((item) => {
return item[key]
});
}
/**
* 提取对象数组的多个属性数组,
* 如 [{'x':1,'y':11,'z':111},{'x':2,'y':22,'z':222}]
* 提取 ['x','y'] 得到[{'x':1,'y':11},{'x':2,'y':22}]
* @param {*} arr
* @param {*} keys
* @returns []
*/
function getArrByKeyMulti(arr, keys = []) {
if (!Array.isArray(arr)) return;
if (!Array.isArray(keys)) return;
let ret = [];
for (let k = 0; k < arr.length; k++) {
let node = {};
for (let j in keys) {
node[keys[j]] = arr[k][keys[j]];
}
ret.push(node);
}
return ret;
}
/**
* 提取对象数组某个键值等于某值的对象数据
* @param {*} arr
* @param {*} key
* @param {*} val
* @returns object {}
*/
function getDataByKey(arr, key, val) {
if (!Array.isArray(arr)) return null;
for (let k = 0; k < arr.length; k++) {
if (arr[k][key] == val)
return arr[k];
}
return null;
}
/**
* 文本内容格式化处理
* @param {*} content
* @param {*} len 截取长度 -1不截取
*/
function fmtText(content, len = -1) {
if (!content) return content;
let str = content.replace(/[\r\n]/g, ""); //去掉回车换行
if (len > 0) {
str = str.substr(0, len);
}
return str.trim();
}
// 下划线转换驼峰
function toHump(name) {
name = name.replace(/\_(\w)/g, function (all, letter) {
return letter.toUpperCase();
});
// 首字母大写
let firstChar = name.charAt(0).toUpperCase();
return firstChar + name.slice(1);
}
// 驼峰转换下划线
function toLine(name) {
name = name.replace(/([A-Z])/g, "_$1").toLowerCase();
//如果首字符为下划线,干掉
if (name.charAt(0) === '_')
return name.slice(1);
else
return name;
}
// 金额格式化 dot为金额每隔三位用","或" "间隔
function fmtMoney(s, dot = ',', prefix = '¥') {
if (s === '' || s === null || s === undefined) s = 0;
s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(2) + "";
var l = s.split(".")[0].split("").reverse(),
r = s.split(".")[1];
t = "";
for (i = 0; i < l.length; i++) {
t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? dot : "");
}
return prefix + t.split("").reverse().join("") + "." + r;
}
/**
*简单数组转对象数组
* @param {*} arr [1,2,3]
* @param {*} key [x1,x2,x3]
* @returns [{x1:1,x2:1,x3:1},{x1:2,x2:2,x3:2},{x1:3,x2:3,x3:3}]
*/
function arr2ObjectArr(arr, key1, key2, key3) {
let ret = [];
for (let k = 0; k < arr.length; k++) {
let obj = {};
if (key1) obj[key1] = arr[k];
if (key2) obj[key2] = arr[k];
if (key3) obj[key3] = arr[k];
ret.push(obj);
}
return ret;
}
/**
* property
* @param {*} property 排序属性
* @returns 排序好的数组
* 用法 arr.sort(compare('age'))
*/
function objArrSortAsc(property) {
return function (a, b) {
var value1 = a[property];
var value2 = b[property];
if (value1 < value2)
return -1;
else if (value1 > value2)
return 1;
else return 0;
}
}
/**
* property
* @param {*} property 排序属性
* @returns 排序好的数组
* 用法 arr.sort(compare('age'))
*/
function objArrSortDesc(property) {
return function (a, b) {
var value1 = a[property];
var value2 = b[property];
if (value1 < value2)
return 1;
else if (value1 > value2)
return -1;
else return 0;
}
}
/**
* 数组有则减少,无则增加
* @param {*} arr
* @param {*} data
* @param {*} sort 排序方式 asc/desc
*/
function arrAddDel(arr, data, sort = 'asc') {
if (!arr) return arr;
if (!Array.isArray(arr)) return arr;
let idx = arr.indexOf(data);
if (idx > -1)
arr.splice(idx, 1);
else
arr.push(data)
if (sort == 'asc')
return arr.sort();
else
return arr.reverse();
}
//数据深度拷贝
function deepClone(data) {
if (data === null || typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || typeof data === 'undefined') {
return data;
}
return JSON.parse(JSON.stringify(data));
}
function padLeft(str, len, charStr) {
if (!str)
str = '';
else
str = str + '';
return new Array(len - str.length + 1).join(charStr || '') + str;
}
function padRight(str, len, charStr) {
if (!str)
str = '';
else
str = str + '';
return str + new Array(len - str.length + 1).join(charStr || '');
}
// 选项表单处理
function getSelectOptions(str) {
if (!str)
return [];
else if (str.includes('=')) {
let arr = str.split(',');
for (let k = 0; k < arr.length; k++) {
let node = arr[k].split('=');
arr[k] = {};
arr[k].label = node[1];
arr[k].val = node[0];
}
return arr;
} else {
return str.split(',');
}
}
// 数组元素交换位置 index1和index2分别是两个数组的索引值
function arraySwap(arr, index1, index2) {
arr[index1] = arr.splice(index2, 1, arr[index1])[0];
return arr;
}
// 数组置顶
function arrayTop(arr, idx) {
let node = arr.splice(idx, 1)[0];
arr.unshift(node);
return arr;
}
// 数组置底
function arrayBottom(arr, idx) {
let node = arr.splice(idx, 1)[0];
arr.push(node);
return arr;
}
/**
* 把某个值/对象按key插到某个对象数组
* @param {*} arr 目标数组
* @param {*} key 键
* @param {*} val 判断值
* @param {*} obj 插入对象{}
*/
function insertObjArrByKey(arr, key, val, obj) {
if (!arr) return arr;
for (let k = 0; k < arr.length; k++) {
if (arr[k][key] == val) {
// 发现存在
arr[k].list.push(obj);
return arr;
}
}
// 不存在
let newObj = {
[key]: val,
list: [obj]
}
arr.push(newObj);
return arr;
}
/**
* 从对象数组中, 根据某个键值 获取满足的对象
* @param {*} arr
* @param {*} key
* @param {*} val
*/
function getValFromArr(arr, key = 'val', val = '') {
if (!Array.isArray(arr)) return null;
for (let k = 0; k < arr.length; k++) {
if (arr[k][key] == val)
return arr[k];
}
return null;
}
// 把字符串按关键字转为数组
function splitTextByKey(txt, key) {
if (txt === null || txt === undefined) return [];
if (key === null || key === undefined || key.trim() == '') return [String(txt)];
key = String(key).trim();
txt = String(txt);
let arr = txt.split(key);
let ret = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] !== '') ret.push(arr[i]);
if (i != (arr.length - 1)) ret.push(key);
}
return ret;
}
module.exports = {
arrayTop,
arraySwap,
arrayBottom,
getTitleByForm,
getValByForm,
dbForms2Obj,
dbFormsFix,
getValFromArr,
getArrByKey,
getArrByKeyMulti, //提取对象数组的多个属性数组
spArr, //拆分一维数组为二维
getDataByKey,
str2Arr,
arr2ObjectArr,
insertObjArrByKey,
arrAddDel,
objArrSortAsc,
objArrSortDesc,
splitTextByKey,
arrAddDel,
isNumber,
padLeft,
padRight,
makeID,
genRandomString, // 随机字符串
genRandomIntString,
genRandomAlpha,
genRandomNum, // 随机数字
fmtText, // 文本内容格式化处理
fmtMoney, //金额格式化
toHump,
toLine,
getSelectOptions, //选项表单处理
deepClone
}
================================================
FILE: cloudfunctions/mcloud/framework/utils/export_util.js
================================================
/**
* Notes: 导出相关函数
* Ver : CCMiniCloud Framework 2.0.14 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-05-25 04:00:00
*/
const cloudBase = require('../../framework/cloud/cloud_base.js');
const cloudUtil = require('../../framework/cloud/cloud_util.js');
const timeUtil = require('../../framework/utils/time_util.js');
const util = require('../../framework/utils/util.js');
const md5Lib = require('../../framework/lib/md5_lib.js');
const config = require('../../config/config.js');
const setupUtil = require('../utils/setup/setup_util.js');
// 获得当前导出链接
async function getExportDataURL(key) {
let url = '';
let time = '';
let expData = await setupUtil.get(key);
if (!expData)
url = '';
else {
url = expData.EXPORT_CLOUD_ID;
url = await cloudUtil.getTempFileURLOne(url) + '?rd=' + timeUtil.time();
time = timeUtil.timestamp2Time(expData.EXPORT_ADD_TIME);
}
return {
url,
time
}
}
// 删除数据文件
async function deleteDataExcel(key) {
console.log('[deleteExcel] BEGIN... , key=' + key)
// 取出数据
let expData = await setupUtil.get(key);
if (!expData) return;
// 文件路径
let xlsPath = expData.EXPORT_CLOUD_ID;
console.log('[deleteExcel] path = ' + xlsPath);
const cloud = cloudBase.getCloud();
await cloud.deleteFile({
fileList: [xlsPath],
}).then(async res => {
console.log(res.fileList);
if (res.fileList && res.fileList[0] && res.fileList[0].status == -503003) {
console.log('[deleteUserExcel] ERROR = ', res.fileList[0].status + ' >> ' + res.fileList[0].errMsg);
this.AppError('文件不存在或者已经删除');
}
// 删除导出数据记录
await setupUtil.remove(key);
console.log('[deleteExcel] OVER.');
}).catch(error => {
if (error.name != 'AppError') {
console.log('[deleteExcel] ERROR = ', error);
this.AppError('操作失败,请重新删除');
} else
throw error;
});
}
// 导出数据
async function exportDataExcel(key, title, total, data, options = {}) {
// 删除导出表
await setupUtil.remove(key);
let fileName = key + '_' + md5Lib.md5(key + config.CLOUD_ID);
let xlsPath = util.getProjectId() + '/' + 'export/' + fileName + '.xlsx';
// 操作excel用的类库
const xlsx = require('node-xlsx');
// 把数据保存到excel里
let buffer = await xlsx.build([{
name: title + timeUtil.time('Y-M-D'),
data,
options
}]);
// 把excel文件保存到云存储里
console.log('[ExportData] Save to ' + xlsPath);
const cloud = cloudBase.getCloud();
let upload = await cloud.uploadFile({
cloudPath: xlsPath,
fileContent: buffer, //excel二进制文件
});
if (!upload || !upload.fileID) return;
// 入导出表
let dataExport = {
EXPORT_ADD_TIME: timeUtil.time(),
EXPORT_KEY: key,
EXPORT_CLOUD_ID: upload.fileID
}
//console.log(dataExport)
await setupUtil.set(key, dataExport, 'export');
console.log('[ExportData] OVER.')
return {
total
}
}
module.exports = {
getExportDataURL,
deleteDataExcel,
exportDataExcel
}
================================================
FILE: cloudfunctions/mcloud/framework/utils/log_util.js
================================================
/**
* Notes: 日志操作函数
* Ver : CCMiniCloud Framework 2.34.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-06-12 04:00:00
*/
const timeUtil = require('./time_util.js');
class LogUtil {
constructor(level = 'info') {
this.logOut = ''; // 输出日志内容
level = level.toLowerCase();
if (level == 'err') level = 'error';
switch (level) {
case 'debug':
level = LogUtil.LEVEL.DEBUG;
break;
case 'info':
level = LogUtil.LEVEL.INFO;
break;
case 'warn':
level = LogUtil.LEVEL.WARN;
break;
case 'error':
level = LogUtil.LEVEL.ERROR;
break;
case 'fatal':
level = LogUtil.LEVEL.FATAL;
break;
case 'none':
level = LogUtil.LEVEL.NONE;
break;
default:
level = LogUtil.LEVEL.INFO;
}
this.level = level;
}
debug(str, ex = '') {
if (this.level > LogUtil.LEVEL.DEBUG) return;
console.debug('[' + this._getTime() + '] DEBUG: ' + str, ex);
this.logOut += "######" + '[' + this._getTime() + '] DEBUG: ' + str + (ex ? JSON.stringify(ex) : '');
}
info(str, ex = '') {
if (this.level > LogUtil.LEVEL.INFO) return;
console.log('[' + this._getTime() + '] INFO: ' + str, ex);
this.logOut += "######" + '[' + this._getTime() + '] INFO: ' + str + (ex ? JSON.stringify(ex) : '');
}
warn(str, ex = '') {
if (this.level > LogUtil.LEVEL.WARN) return;
console.warn('[' + this._getTime() + '] WARN: ' + str, ex);
this.logOut += "######" + '[' + this._getTime() + '] WARN: ' + str + (ex ? JSON.stringify(ex) : '');
}
error(str, ex = '') {
if (this.level > LogUtil.LEVEL.ERROR) return;
console.error('[' + this._getTime() + '] ERROR: ' + str, ex);
this.logOut += "######" + '[' + this._getTime() + '] ERROR: ' + str + (ex ? JSON.stringify(ex) : '');
}
fatal(str, ex = '') {
if (this.level > LogUtil.LEVEL.FATAL) return;
console.error('[' + this._getTime() + '] FATAL: ' + str, ex);
this.logOut += "######" + '[' + this._getTime() + '] FATAL: ' + str + (ex ? JSON.stringify(ex) : '');
}
_getTime() {
return timeUtil.time('Y-M-D h:m:s');
}
err(str) {
error(str);
}
getLogOut() {
return this.logOut;
}
}
LogUtil.LEVEL = {
DEBUG: 10,
INFO: 20,
WARN: 30,
ERROR: 40,
FATAL: 50,
NONE: 100,
};
module.exports = LogUtil;
================================================
FILE: cloudfunctions/mcloud/framework/utils/math_util.js
================================================
/**
* Notes: 数学计算相关操作函数
* Ver : CCMiniCloud Framework 2.35.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-10-04 04:00:00
*/
/** 获取百分比, 保留2位小数 */
function percent(num1, num2) {
return Math.round(num1 / num2 * 10000) / 100.00;
}
/** 数组对象排序 */
function arrayObjecSortAsc(property) {
return function (a, b) {
var value1 = a[property];
var value2 = b[property];
return value1 - value2;
}
}
/** 数组对象排序 */
function arrayObjecSortDesc(property) {
return function (a, b) {
var value1 = a[property];
var value2 = b[property];
return value2 - value1;
}
}
module.exports = {
percent, // 百分比,保留2位小数
arrayObjecSortAsc, // 数组对象排序
arrayObjecSortDesc, // 数组对象排序
}
================================================
FILE: cloudfunctions/mcloud/framework/utils/setup/setup_model.js
================================================
/**
* Notes: 系统设置实体
* Ver : CCMiniCloud Framework 2.0.15 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-05 19:20:00
*/
const MultiModel = require('../../database/multi_model.js');
class SetupModel extends MultiModel {
}
// 集合名
SetupModel.CL = MultiModel.C('setup');
SetupModel.DB_STRUCTURE = {
_pid: 'string|true',
SETUP_ID: 'string|true',
SETUP_TYPE: 'string|false', //content/cache/vouch
SETUP_KEY: 'string|true',
SETUP_VALUE: 'object|true', // {val:}
SETUP_ADD_TIME: 'int|true',
SETUP_EDIT_TIME: 'int|true',
SETUP_ADD_IP: 'string|false',
SETUP_EDIT_IP: 'string|false',
};
// 字段前缀
SetupModel.FIELD_PREFIX = "SETUP_";
module.exports = SetupModel;
/*
### 富文本
[{"type":"text","val":"xxx"},{"type":"img","val":"cloudId://xxxx"}]
### 导出
{"EXPORT_CLOUD_ID":"","EXPORT_EDIT_TIME":""}
*/
================================================
FILE: cloudfunctions/mcloud/framework/utils/setup/setup_util.js
================================================
/**
* Notes: 系统设置相关函数
* Ver : CCMiniCloud Framework 2.31.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-05-25 04:00:00
*/
const SetupModel = require('./setup_model.js');
/**
* 设置
* key 键key
* val 值value
* t 秒
*/
async function set(key, val, type = '') {
if (!key) return null;
let where = {
SETUP_KEY: key
}
let data = {
SETUP_TYPE: type,
SETUP_VALUE: {
val
},
}
await SetupModel.insertOrUpdate(where, data);
}
/**
* 获取
* k 键key
* def 默认值
*/
async function get(key) {
if (!key) return null;
let where = {
SETUP_KEY: key
}
let setup = await SetupModel.getOne(where, 'SETUP_VALUE');
if (!setup) return null;
let res = setup.SETUP_VALUE.val;
if (res === undefined) {
return null;
} else {
return res;
}
}
async function get(key) {
if (!key) return null;
let where = {
SETUP_KEY: key
}
let setup = await SetupModel.getOne(where, 'SETUP_VALUE');
if (!setup) return null;
let res = setup.SETUP_VALUE.val;
if (res === undefined) {
return null;
} else {
return res;
}
}
async function remove(key, fuzzy = false) {
if (!key) return;
let where = {
SETUP_KEY: key
}
if (fuzzy) {
where.SETUP_KEY = {
$regex: '.*' + key,
$options: 'i'
};
}
await SetupModel.del(where);
}
module.exports = {
set,
get,
remove
}
================================================
FILE: cloudfunctions/mcloud/framework/utils/time_util.js
================================================
/**
* Notes: 时间相关函数
* Ver : CCMiniCloud Framework 2.36.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const util = require('./util.js');
/** 日期简化,去掉多余的前缀0 */
function simpleDate(date) {
let arr = date.split('-');
if (arr.length < 3) return date;
let month = arr[1];
if (month.indexOf('0') == 0)
month = month.replace('0', '');
let day = arr[2];
if (day.indexOf('0') == 0)
day = day.replace('0', '');
return arr[0] + '-' + month + '-' + day;
}
/** 时间格式化为年月日点分 */
function fmtDateCHN(date, fmt = 'Y-M-D') {
if (!date) return '';
if (fmt == 'hh:mm' && date.includes(':')) {
if (date.includes(' ')) date = date.split(' ')[1];
let arr = date.split(':');
return Number(arr[0]) + '点' + arr[1] + '分';
} else if (fmt == 'Y-M-D hh:mm') {
let arr = date.split(' ');
if (arr.length != 2) return date;
return fmtDateCHN(arr[0], 'Y-M-D') + fmtDateCHN(arr[1], 'hh:mm');
} else if (fmt == 'M-D hh:mm') {
let arr = date.split(' ');
if (arr.length != 2) return date;
return fmtDateCHN(arr[0], 'M-D') + ' ' + fmtDateCHN(arr[1], 'hh:mm');
} else {
if (date.includes(' ')) date = date.split(' ')[0];
let arr = date.split('-');
if (fmt == 'Y-M') //年月
return arr[0] + '年' + Number(arr[1]) + '月';
else if (fmt == 'M-D') //月日
return arr[1] + '月' + Number(arr[2]) + '日';
else if (fmt == 'Y') //年
return arr[0] + '年';
else
return arr[0] + '年' +Number(arr[1]) + '月' + Number(arr[2]) + '日';
}
}
/**
* 毫秒时间戳转时间格式
* @param {*} unixtime 毫秒
* @param {*} format Y-M-D h:m:s
* @param {*} diff 时区差异 毫秒
*/
function timestamp2Time(unixtime, format = 'Y-M-D h:m:s', diff = 0) {
unixtime = Number(unixtime);
let formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
let returnArr = [];
let date = new Date(unixtime + diff);
returnArr.push(date.getFullYear());
returnArr.push(formatNumber(date.getMonth() + 1));
returnArr.push(formatNumber(date.getDate()));
returnArr.push(formatNumber(date.getHours()));
returnArr.push(formatNumber(date.getMinutes()));
returnArr.push(formatNumber(date.getSeconds()));
for (let i in returnArr) {
format = format.replace(formateArr[i], returnArr[i]);
}
return format;
}
function timestame2Ago(dateTimeStamp, fmt = 'Y-M-D', diff = 0) { //dateTimeStamp是一个时间毫秒,注意时间戳是秒的形式,在这个毫秒的基础上除以1000,就是十位数的时间戳。13位数的都是时间毫秒。
let minute = 1000 * 60; //把分,时,天,周,半个月,一个月用毫秒表示
let hour = minute * 60;
let day = hour * 24;
let week = day * 7;
let month = day * 30;
let now = new Date().getTime(); //获取当前时间毫秒
let diffValue = now - dateTimeStamp; //时间差
if (diffValue < 0) {
return;
}
let minC = diffValue / minute; //计算时间差的分,时,天,周,月
let hourC = diffValue / hour;
let dayC = diffValue / day;
let result = '';
let weekC = diffValue / week;
let monthC = diffValue / month;
if (monthC >= 1 && monthC <= 3) {
result = ' ' + parseInt(monthC) + '月前'
} else if (weekC >= 1 && weekC <= 3) {
result = ' ' + parseInt(weekC) + '周前'
} else if (dayC >= 1 && dayC <= 6) {
result = ' ' + parseInt(dayC) + '天前'
} else if (hourC >= 1 && hourC <= 23) {
result = ' ' + parseInt(hourC) + '小时前'
} else if (minC >= 1 && minC <= 59) {
result = ' ' + parseInt(minC) + '分钟前'
} else if (diffValue >= 0 && diffValue <= minute) {
result = '刚刚'
} else {
result = timestamp2Time(dateTimeStamp, fmt, diff);
}
return result;
}
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
/**
* 时间转时间戳
* @param {*} date 支持 Y-M-D h:m:s / Y-M-D
*/
function time2Timestamp(date) {
if (date.length < 10) {
let arr = date.split('-');
if (arr[1].length == 1) arr[1] = '0' + arr[1];
if (arr[2].length == 1) arr[2] = '0' + arr[2];
date = arr[0] + '-' + arr[1] + '-' + arr[2];
}
if (date.length == 10) date = date + ' 00:00:00';
let d = new Date(date.replace(/-/g, '/'));
return d.getTime();
}
/**
* 获取当前时间戳/时间Y-M-D h:m:s
* @param {*} 时间格式 Y-M-D h:m:s
* @param {int} 时间步长 (秒)
*/
function time(fmt, step = 0) {
let t = 0;
if (util.isDefined(fmt)) {
let t = new Date().getTime() + step * 1000;
return timestamp2Time(t, fmt);
}
return new Date().getTime() + t * 1000;
}
// 获取某天0点
function getDayFirstTimestamp(timestamp) {
if (!timestamp) timestamp = time();
return time2Timestamp(timestamp2Time(timestamp, 'Y-M-D'));
}
/**
* 根据出生日期计算年龄周岁 传参格式为1996-06-08
* @param {*} birth
*/
function getAge(birth, isMonth = false) {
var returnAge = '';
var mouthAge = '';
var arr = birth.split('-');
var birthYear = arr[0];
var birthMonth = arr[1];
var birthDay = arr[2];
var d = new Date();
var nowYear = d.getFullYear();
var nowMonth = d.getMonth() + 1;
var nowDay = d.getDate();
if (nowYear == birthYear) {
// returnAge = 0; //同年 则为0岁
var monthDiff = nowMonth - birthMonth; //月之差
if (monthDiff < 0) {} else {
mouthAge = monthDiff + '个月';
}
} else {
var ageDiff = nowYear - birthYear; //年之差
if (ageDiff > 0) {
if (nowMonth == birthMonth) {
var dayDiff = nowDay - birthDay; //日之差
if (dayDiff < 0) {
returnAge = ageDiff - 1 + '岁';
} else {
returnAge = ageDiff + '岁';
}
} else {
var monthDiff = nowMonth - birthMonth; //月之差
if (monthDiff < 0) {
returnAge = ageDiff - 1 + '岁';
} else {
mouthAge = monthDiff + '个月';
returnAge = ageDiff + '岁';
}
}
} else {
returnAge = -1; //返回-1 表示出生日期输入错误 晚于今天
}
}
if (isMonth)
return returnAge + mouthAge; //返回周岁年龄+月份
else
return returnAge;
}
/**
* 日期计算周几
* @param {*} day 日期为输入日期,格式为 2013-03-10
*/
function week(day) {
if (!day || !day.includes('-')) return '';
let arys1 = new Array();
arys1 = day.split('-');
let ssdate = new Date(arys1[0], parseInt(arys1[1] - 1), arys1[2]);
let week1 = String(ssdate.getDay()).replace("0", "日").replace("1", "一").replace("2", "二").replace("3", "三").replace("4", "四").replace("5", "五").replace("6", "六") //就是你要的星期几
return "周" + week1; //就是你要的星期几
}
/** 获取某天所在某月第一天时间戳 */
function getMonthFirstTimestamp(timestamp) {
let inDate = new Date(timestamp);
let year = inDate.getFullYear();
let month = inDate.getMonth();
return new Date(year, month, 1).getTime();
}
/** 获取某天所在某月最后一天时间戳 */
function getMonthLastTimestamp(timestamp) {
let inDate = new Date(timestamp);
let year = inDate.getFullYear();
let month = inDate.getMonth();
return new Date(year, month + 1, 1).getTime() - 1;
}
// 取得分钟时间戳
function getNowMinTimestamp() {
let min = time('Y-M-D h:m') + ':00';
let timestamp = time2Timestamp(min);
return {
min,
timestamp
}
}
// 获取当前日期所在周一 输入和返回格式=yyyy-mm-dd
function getFirstOfWeek(date) {
let now = new Date(date);
let nowTime = now.getTime();
let day = now.getDay();
if (day == 0) day = 7;
let oneDayTime = 24 * 60 * 60 * 1000;
let mondayTime = nowTime - (day - 1) * oneDayTime;
return timestamp2Time(mondayTime, 'Y-M-D');
}
// 获取当前日期所在周一 输入和返回格式=yyyy-mm-dd
function getLastOfWeek(date) {
let now = new Date(date);
let nowTime = now.getTime();
let day = now.getDay();
if (day == 0) day = 7;
let oneDayTime = 24 * 60 * 60 * 1000;
let sundayTime = nowTime + (7 - day) * oneDayTime;
return timestamp2Time(sundayTime, 'Y-M-D');
}
// 获取当前日期所在月第一天 输入和返回格式=yyyy-mm-dd
function getFirstOfMonth(date) {
let arr = date.split('-');
return arr[0] + '-' + arr[1] + '-01';
}
// 获取当前日期所在月最后一天 输入和返回格式=yyyy-mm-dd
function getLastOfMonth(date) {
let now = new Date(date);
let y = now.getFullYear();
let m = now.getMonth();
let lastDay = new Date(y, m + 1, 0).getTime();
return timestamp2Time(lastDay, 'Y-M-D');
}
/**
* 取倒计时(天时分秒) 支持时间戳或者Y-M-D/Y-M-D h:m:s
* @param {*} datetimeTo
* @param {*} flag 1=正 -1=负
*/
function getTimeLeft(datetimeTo, flag = 1) {
let time1 = datetimeTo;
if (String(datetimeTo).includes('-')) {
datetimeTo = String(datetimeTo);
if (!datetimeTo.includes(':'))
datetimeTo += ' 00:00:00';
time1 = new Date(datetimeTo).getTime();
}
let time2 = new Date().getTime();
let mss = time1 - time2;
// 将时间差(毫秒)格式为:天时分秒
let days = parseInt(mss / (1000 * 60 * 60 * 24));
let hours = parseInt((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
let minutes = parseInt((mss % (1000 * 60 * 60)) / (1000 * 60));
let seconds = parseInt((mss % (1000 * 60)) / 1000);
if (mss < 0 && mss < -86400 * 1000) {
(days != 0) ? days = -flag * days + "天": days = '';
return days + "前";
} else if (mss < 0) {
return "今天";
} else {
(days != 0) ? days = flag * days + "天": days = '';
(hours != 0) ? hours = flag * hours + "时": hours = '';
(minutes != 0) ? minutes = flag * minutes + "分": minutes = '';
return days + hours + minutes + flag * seconds + "秒"
}
}
module.exports = {
fmtDateCHN,
simpleDate,
getTimeLeft,
getNowMinTimestamp,
getMonthFirstTimestamp,
getMonthLastTimestamp,
getDayFirstTimestamp,
timestamp2Time,
timestame2Ago,
time2Timestamp,
time,
getAge,
week, //星期
getFirstOfWeek,
getLastOfWeek,
getFirstOfMonth,
getLastOfMonth
}
================================================
FILE: cloudfunctions/mcloud/framework/utils/util.js
================================================
/**
* Notes: 通用工具函数
* Ver : CCMiniCloud Framework 2.38.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
function getProjectId() {
if (global.PID)
return global.PID;
else
return 'ONE';
}
/**
* 判断变量,参数,对象属性是否定义
* @param {*} val
*/
function isDefined(val) {
// == 不能判断是否为null
if (val === undefined)
return false;
else
return true;
}
/**
* 判断对象是否为空
* @param {*} obj
*/
function isObjectNull(obj) {
return (Object.keys(obj).length == 0);
}
/**
* 休眠时间,配合await使用
* @param {*} time 毫秒
*/
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
};
module.exports = {
getProjectId,
isDefined, //判断变量,参数,对象属性是否定义
sleep,
isObjectNull,
}
================================================
FILE: cloudfunctions/mcloud/framework/validate/content_check.js
================================================
/**
* Notes: 内容审核
* Ver : CCMiniCloud Framework 2.39.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const AppError = require('../core/app_error.js');
const cloudBase = require('../cloud/cloud_base.js');
const config = require('../../config/config.js');
/**
* 前台校验
* @param {*} imgData
* @param {*} mine
*/
async function checkImgClient(imgData, mine) {
if (!config.CLIENT_CHECK_CONTENT) return;
return await checkImg(imgData, mine);
}
/**
* 后台校验
* @param {*} imgData
* @param {*} mine
*/
async function checkImgAdmin(imgData, mine) {
if (!config.ADMIN_CHECK_CONTENT) return;
return await checkImg(imgData, mine);
}
/**
* 校验图片信息
* @param {*} 图片流buffer
*/
async function checkImg(imgData, mine) {
let cloud = cloudBase.getCloud();
try {
const result = await cloud.openapi.security.imgSecCheck({
media: {
contentType: 'image/' + mine,
value: Buffer.from(imgData, 'base64') // 这里必须要将小程序端传过来的进行Buffer转化,否则就会报错,接口异常
}
})
console.log('imgcheck', result);
if (!result || result.errCode !== 0) {
throw new AppError('图片内容不合适,请修改');
}
} catch (err) {
console.log('imgcheck ex', err);
throw new AppError('图片内容不合适,请修改');
}
}
/**
* 后台把输入数据里的文本数据提交内容审核
* @param {*} input
*/
async function checkTextMultiAdmin(input) {
if (!config.ADMIN_CHECK_CONTENT) return;
return checkTextMulti(input);
}
/**
* 前台把输入数据里的文本数据提交内容审核
* @param {*} input
*/
async function checkTextMultiClient(input) {
if (!config.CLIENT_CHECK_CONTENT) return;
return checkTextMulti(input);
}
/**
* 把输入数据里的文本数据提交内容审核
* @param {*} input
*/
async function checkTextMulti(input) {
let txt = '';
for (let key in input) {
if (typeof (input[key]) === 'string')
txt += input[key];
else if (typeof (input[key]) === 'object') //包括数组和对象
txt += JSON.stringify(input[key]);
}
await checkText(txt);
}
/**
* 后台校验文字信息
* @param {*}
*/
async function checkTextAdmin(txt) {
if (!config.ADMIN_CHECK_CONTENT) return;
return checkText(txt);
}
/**
* 前台校验文字信息
* @param {*}
*/
async function checkTextClient(txt) {
if (!config.CLIENT_CHECK_CONTENT) return;
return checkText(txt);
}
/**
* 校验文字信息
* @param {*}
*/
async function checkText(txt) {
if (!txt) return;
let cloud = cloudBase.getCloud();
try {
const result = await cloud.openapi.security.msgSecCheck({
content: txt
})
if (!result || result.errCode !== 0) {
throw new AppError('文字内容不合适,请修改或者重试');
}
} catch (err) {
console.log('checkText ex', err);
throw new AppError('文字内容不合适,请修改或者重试');
}
}
module.exports = {
checkImg,
checkImgClient,
checkImgAdmin,
checkTextMulti,
checkTextMultiClient,
checkTextMultiAdmin,
checkText,
checkTextClient,
checkTextAdmin
}
================================================
FILE: cloudfunctions/mcloud/framework/validate/data_check.js
================================================
/**
* Notes: 数据校验类库
* Ver : CCMiniCloud Framework 2.21.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-01-07 07:48:00
*
*/
const AppError = require('../core/app_error.js');
const appCode = require('../core/app_code.js');
const CHECK_OPEN = true;
const CHECK_SOURCE = 'admin'; //client/admin
/**
* 判断变量,参数,对象属性是否定义
* @param {*} val
*/
function isDefined(val) {
// == 不能判断是否为null
if (val === undefined)
return false;
else
return true;
}
function isNull(value) {
if (value === null || value === undefined) return true;
if (getDataType(value) == String && value === '') return true;
return false;
}
function isStrAndArrNull(value) {
if (value === null || value === undefined) return true;
let type = getDataType(value);
if (type == String && value === '') return true;
if (type == Array && value.length == 0) return true;
return false;
}
function isRealNull(value) {
if (value === null || value === undefined) return true;
let type = getDataType(value);
if (type == String && value === '') return true;
if (type == Array && value.length == 0) return true;
if (type == Object && JSON.stringify(value) == '{}') return true;
return false;
}
function getDataType(value) {
if (value === null || value === undefined) return value;
return value.constructor;
}
// 是否必填
function checkRequired(value, desc = '') {
switch (getDataType(value)) {
case Object:
if (JSON.stringify(value) == '{}')
return desc + '不能为空obj';
break;
case Array:
if (value.length == 0)
return desc + '不能为空arr';
break;
case String:
if (value.length == 0)
return desc + '不能为空';
break;
case null:
case undefined:
return desc + '不能为空';
}
}
// 校验字符/数组长度,校验数字大小
function checkMin(value, min, desc = '') {
if (isStrAndArrNull(value)) return;
min = Number(min);
switch (getDataType(value)) {
case Array:
if (value.length < min)
return desc + '不能少于' + min + '项';
break;
case String:
if (value.length < min)
return desc + '不能少于' + min + '位';
break;
case Number:
if (value < min)
return desc + '不能小于' + min;
break;
}
};
// 校验字符/数组长度,校验数字大小
function checkMax(value, max, desc = '') {
if (isStrAndArrNull(value)) return;
max = Number(max);
switch (getDataType(value)) {
case Array:
if (value.length > max)
return desc + '不能多于' + max + '项';
break;
case String:
if (value.length > max)
return desc + '不能多于' + max + '位';
break;
case Number:
if (value > max)
return desc + '不能大于' + max;
break;
}
};
// 校验字符/数组长度
function checkLen(value, len, desc = '') {
if (isStrAndArrNull(value)) return;
len = Number(len);
switch (getDataType(value)) {
case Array:
if (value.length != len)
return desc + '必须为' + len + '项';
break;
case String:
if (value.length != len)
return desc + '必须为' + len + '位';
break;
}
};
function checkMobile(value, desc = '') {
if (isNull(value)) return;
if (!/(^1[1|2|3|4|5|6|7|8|9][0-9]{9}$)/.test(value))
return desc + '格式不正确';
}
function checkInt(value, desc = '') {
if (isNull(value)) return;
if (!/^[0-9]+$/.test(value))
return desc + '必须为数字';
}
function checkDigit(value, desc = '') {
if (isNull(value)) return;
if (!/^\d+(\.\d+)?$/.test(value))
return desc + '必须为数字或小数';
}
function checkLetter(value, desc = '') {
if (isNull(value)) return;
if (!/^[A-Za-z]+$/.test(value))
return desc + '必须为字母';
}
function checkMoney(value, desc = '') {
if (isNull(value)) return;
if (!/(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/.test(value))
return desc + '必须为金额格式,例如2.00';
}
function checkLetterNum(value, desc = '') {
if (isNull(value)) return;
if (!/^\w+$/.test(value))
return desc + '必须为字母,数字和下划线';
}
function checkId(value, desc = '', min = 1, max = 100) {
if (isNull(value)) return;
min = Number(min);
max = Number(max);
if (getDataType(value) != String) return desc + '必须为ID字符串格式';
if (value.length < min || value.length > max) return desc + '必须为ID格式';
/*if (!/^\w+$/.test(value))
return desc + '必须为ID格式';*/
}
// 邮箱
function checkEmail(value, desc = '') {
if (isNull(value)) return;
let reg = /^[A-Za-z0-9+]+[A-Za-z0-9\.\_\-+]*@([A-Za-z0-9\-]+\.)+[A-Za-z0-9]+$/;
if (!reg.test(value)) return desc + '必须为邮箱格式';
}
// 短日期,形如 (yyyy-mm-dd 2008-07-22)
function checkDate(value, desc = '') {
if (isNull(value)) return;
let hint = '请选择' + desc;
if (value.length != 10) return hint;
let r = value.match(/^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/);
if (r == null) return hint;
let d = new Date(r[1], r[3] - 1, r[4]);
let chk = d.getFullYear() == r[1] && (d.getMonth() + 1) == r[3] && d.getDate() == r[4];
if (!chk) return hint;
}
// 年份,形如 (yyyy 2008)
function checkYear(value, desc = '') {
if (isNull(value)) return;
let hint = '请选择' + desc;
if (value.length != 4) return hint;
value += '-01-01';
return checkDate(value, desc);
}
// 年月,形如 (yyyy-mm 2008-01)
function checkYearMonth(value, desc = '') {
if (isNull(value)) return;
let hint = '请选择' + desc;
if (value.length != 7) return hint;
value += '-01';
return checkDate(value, desc);
}
// 短时间(时分秒),形如 (13:04:06)
function checkTime(value, desc = '') {
if (isNull(value)) return;
let hint = desc + '必须为时间格式';
if (value.length != 8) return hint;
let a = value.match(/^(\d{1,2})(:)?(\d{1,2})\2(\d{1,2})$/);
if (a == null) return hint;
if (a[1] > 23 || a[3] > 59 || a[4] > 59) return hint;
}
// 短时间(时分),形如 (hh:mm 13:04)
function checkHourMinute(value, desc = '') {
if (isNull(value)) return;
let hint = desc + '必须为时分时间格式';
if (value.length != 5) return hint;
value += ':01';
return checkTime(value, desc);
}
// 长时间,形如 (2008-07-22 13:04:06)
function checkDatimeTime(value, desc = '') {
if (isNull(value)) return;
let hint = desc + '必须为完整时间格式';
if (value.length != 19) return hint;
var reg = /^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/;
var r = value.match(reg);
if (r == null) return hint;
var d = new Date(r[1], r[3] - 1, r[4], r[5], r[6], r[7]);
let chk = d.getFullYear() == r[1] && (d.getMonth() + 1) == r[3] && d.getDate() == r[4] && d.getHours() == r[5] && d.getMinutes() == r[6] && d.getSeconds() == r[7];
if (!chk) return hint;
}
function checkArray(value, desc = '') {
if (!Array.isArray(value))
return desc + '填写错误arr';
}
function checkObject(value, desc = '') {
if (value.constructor != Object)
return desc + '填写错误obj';
}
function checkBoolean(value, desc = '') {
if (value.constructor != Boolean)
return desc + '填写错误bool';
}
// 枚举 ref=1,2,3,4格式
function checkIn(value, ref, desc = '') {
if (isNull(value)) return;
let type = getDataType(value);
if (type != String && type != Number) return desc + '填写范围错误';
let arr = String(ref).split(',');
if (!arr.includes(value) && !arr.includes(value + ''))
return desc + '填写范围错误';
}
function checkIds(value, desc) {}
function checkString(value, desc) {
if (value.constructor != String)
return desc + '填写错误';
}
function check(data, rules, that) {
let returnData = {};
for (let key in rules) {
let arr = rules[key].split('|');
let desc = key; // 字段说明
let defVal = undefined; // 缺省值
let dataType = 'String'; //数据类型
if (!CHECK_OPEN) { // 不校验
// 取值
let val = data[formName];
returnData[key] = val;
continue;
}
// 小循环获取规则
for (let i = 0; i < arr.length; i++) {
// 数据项说明
if (arr[i].startsWith('name=')) {
desc = '「' + arr[i].replace('name=', '') + '」';
continue;
}
// 缺省值
if (arr[i].startsWith('default=')) {
defVal = arr[i].replace('default=', '').trim();
continue;
}
// 数据类型
switch (arr[i].toLowerCase()) {
case 'int':
case 'digit':
dataType = 'Number';
break;
case 'array':
case 'arr':
dataType = 'Array';
break;
case 'object':
case 'obj':
dataType = 'Object';
break;
case 'bool':
case 'boolean':
dataType = 'Boolean';
break;
}
}
// 校验
let formName = (CHECK_SOURCE == 'admin') ? key : arr[0]; // 表单名 admin/client
// 取值
let val = data[formName];
switch (dataType) {
case 'Array': {
if (defVal !== undefined) {
try {
defVal = JSON.parse(defVal);
if (getDataType(defVal) != Array)
return _showError(desc + '默认值数组格式错误', formName, that);
} catch (ex) {
return _showError(desc + '默认值数组格式错误', formName, that);
}
}
if (val === null || val === undefined) val = defVal;
if (val !== undefined && getDataType(val) != Array)
return _showError(desc + '数组格式错误', formName, that);
break;
}
case 'Object': {
if (defVal !== undefined) {
try {
defVal = JSON.parse(defVal);
if (getDataType(defVal) != Object)
return _showError(desc + '默认值对象格式错误', formName, that);
} catch (ex) {
return _showError(desc + '默认值对象格式错误', formName, that);
}
}
if (val === null || val === undefined) val = defVal;
if (val !== undefined && getDataType(val) != Object)
return _showError(desc + '对象格式错误', formName, that);
break;
}
case 'Boolean': {
if (defVal !== undefined) {
try {
defVal = JSON.parse(defVal);
if (getDataType(defVal) != Boolean)
return _showError(desc + '默认值布尔格式错误', formName, that);
} catch (ex) {
return _showError(desc + '默认值布尔格式错误');
}
}
if (val === null || val === undefined) val = defVal;
if (val !== undefined && getDataType(val) != Boolean)
return _showError(desc + '布尔格式错误', formName, that);
break;
}
case 'Number': {
if (checkDigit(defVal, desc + '默认值'))
return _showError(desc + '默认值格式错误', formName, that);
if (val === null || val === undefined) val = defVal;
if (val === undefined) break;
if (val === '') //数字不能为空
return _showError(desc + '不能为空', formName, that);
let dataType = getDataType(val);
if (dataType == Object || dataType == Boolean || dataType == Array)
return _showError(desc + '必须为数字格式', formName, that);
// 数字格式校验
let result = checkDigit(val, desc);
if (result) return _showError(result, formName, that);
val = Number(val);
break;
}
case 'String': {
let dataType = getDataType(val);
if (dataType == Object || dataType == Boolean || dataType == Array)
return _showError(desc + '必须为字符串格式', formName, that);
if (val === null || val === undefined) val = defVal;
if (val === undefined) break;
try {
val = String(val).trim(); // 数字会被转为字符串
} catch (ex) {
return _showError(desc + '必须为字符串格式', formName, that);
}
break;
}
}
returnData[key] = val;
let fromStep = (CHECK_SOURCE == 'admin') ? 0 : 1; //admin/client
for (let i = fromStep; i < arr.length; i++) {
let result = '';
let rules = arr[i].split(':');
let ruleName = rules[0];
// 空 且非必填的 不校验
if (ruleName != 'must' && val === undefined) continue;
switch (ruleName) {
case 'must':
result = checkRequired(val, desc);
break;
case 'str':
case 'string':
result = checkString(val, desc);
break;
case 'arr':
case 'array':
result = checkArray(val, desc);
break;
case 'obj':
case 'object':
result = checkObject(val, desc);
break;
case 'bool':
case 'boolean':
result = checkBoolean(val, desc);
break;
case 'money':
result = checkMoney(val, desc);
break;
case 'year':
result = checkYear(val, desc);
break;
case 'yearmonth':
result = checkYearMonth(val, desc);
break;
case 'date':
result = checkDate(val, desc);
break;
case 'time':
result = checkTime(val, desc);
break;
case 'hourminute':
result = checkHourMinute(val, desc);
break;
case 'datetime':
result = checkDatimeTime(val, desc);
break;
case 'min':
result = checkMin(val, Number(rules[1]), desc);
break;
case 'max':
result = checkMax(val, Number(rules[1]), desc);
break;
case 'len':
result = checkLen(val, Number(rules[1]), desc);
break;
case 'in':
result = checkIn(val, rules[1], desc);
break;
case 'email':
result = checkEmail(val, desc);
break;
case 'mobile':
result = checkMobile(val, desc);
break;
case 'int': // 正整数
result = checkInt(val, desc);
break;
case 'digit': // 正小整数
result = checkDigit(val, desc);
break;
case 'id':
result = checkId(val, desc);
break;
case 'letter':
result = checkLetter(val, desc);
break;
case 'letter_num':
result = checkLetterNum(val, desc);
break;
}
if (result) {
_showError(result, formName, that);
return false;
} else {
if (that) {
if (CHECK_SOURCE == 'client') {
// 删除原有的自动聚焦 //admin/client
if (isDefined(that.data[formName + 'Focus'])) {
that.setData({ //TODO delete?
[formName + 'Focus']: false
});
}
}
}
}
}
}
return returnData;
}
function _showError(result, formName, that) { //admin/client
if (CHECK_SOURCE == 'client') {
wx.showModal({
title: '温馨提示',
content: result,
showCancel: false,
success(res) {
// 自动聚焦
if (that) {
pageHelper.anchor(formName, that);
that.setData({
[formName + 'Focus']: result,
});
}
}
});
} else {
throw new AppError(result, appCode.DATA);
}
}
module.exports = {
check,
checkString,
checkArray,
checkObject,
checkMoney,
checkYear,
checkYearMonth,
checkDate,
checkTime,
checkHourMinute,
checkDatimeTime,
checkMin,
checkMax,
checkLen,
checkIn,
checkEmail,
checkMobile,
checkInt, // 正小整数
checkDigit,
checkId,
checkLetter,
checkLetterNum,
}
================================================
FILE: cloudfunctions/mcloud/index.js
================================================
const application = require('./framework/core/application.js');
// 云函数入口函数
exports.main = async (event, context) => {
return await application.app(event, context);
}
================================================
FILE: cloudfunctions/mcloud/package.json
================================================
{
"name": "cloud",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"date-utils": "^1.2.21",
"mysql": "^2.18.1",
"node-xlsx": "^0.16.1",
"wx-server-sdk": "~2.1.2"
}
}
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_album_controller.js
================================================
/**
* Notes: 相册模块后台管理-控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 10:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const AdminAlbumService = require('../../service/admin/admin_album_service.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const contentCheck = require('../../../../framework/validate/content_check.js');
const AlbumModel = require('../../model/album_model.js');
class AdminAlbumController extends BaseProjectAdminController {
/** 置顶与排序设定 */
async sortAlbum() {
await this.isAdmin();
let rules = {
id: 'must|id',
sort: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminAlbumService();
await service.sortAlbum(input.id, input.sort);
}
/** 首页设定 */
async vouchAlbum() {
await this.isAdmin();
let rules = {
id: 'must|id',
vouch: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminAlbumService();
await service.vouchAlbum(input.id, input.vouch);
}
/** 状态修改 */
async statusAlbum() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
status: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminAlbumService();
await service.statusAlbum(input.id, input.status);
}
/** 列表 */
async getAdminAlbumList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
whereEx: 'object|name=附加查询条件',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminAlbumService();
let result = await service.getAdminAlbumList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].ALBUM_ADD_TIME = timeUtil.timestamp2Time(list[k].ALBUM_ADD_TIME, 'Y-M-D h:m:s');
if (list[k].ALBUM_OBJ && list[k].ALBUM_OBJ.detail)
delete list[k].ALBUM_OBJ.detail;
}
result.list = list;
return result;
}
/** 发布 */
async insertAlbum() {
await this.isAdmin();
// 数据校验
let rules = {
title: 'must|string|min:2|max:50|name=标题',
cateId: 'must|string|name=分类',
cateName: 'must|string|name=分类名称',
order: 'must|int|min:0|max:9999|name=排序号',
forms: 'array|name=表单',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminAlbumService();
let result = await service.insertAlbum(input);
this.logOther('添加了《' + input.title + '》');
return result;
}
/** 获取信息用于编辑修改 */
async getAlbumDetail() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminAlbumService();
return await service.getAlbumDetail(input.id);
}
/** 编辑 */
async editAlbum() {
await this.isAdmin();
let rules = {
id: 'must|id',
title: 'must|string|min:2|max:50|name=标题',
cateId: 'must|string|name=分类',
cateName: 'must|string|name=分类名称',
order: 'must|int|min:0|max:9999|name=排序号',
forms: 'array|name=表单',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminAlbumService();
let result = service.editAlbum(input);
this.logOther('修改了《' + input.title + '》');
return result;
}
/** 删除 */
async delAlbum() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let title = await AlbumModel.getOneField(input.id, 'ALBUM_TITLE');
let service = new AdminAlbumService();
await service.delAlbum(input.id);
if (title)
this.logOther('删除了《' + title + '》');
}
/** 更新图片信息 */
async updateAlbumForms() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
hasImageForms: 'array'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminAlbumService();
return await service.updateAlbumForms(input);
}
}
module.exports = AdminAlbumController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_home_controller.js
================================================
/**
* Notes: 后台登录与首页模块
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-03-15 19:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const AdminHomeService = require('../../service/admin/admin_home_service.js');
class AdminHomeController extends BaseProjectAdminController {
// 管理首页
async adminHome() {
await this.isAdmin();
// 数据校验
let rules = {
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminHomeService();
return await service.adminHome();
}
// 清除首页推荐
async clearVouchData() {
await this.isAdmin();
// 数据校验
let rules = {
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminHomeService();
return await service.clearVouchData();
}
}
module.exports = AdminHomeController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_meet_controller.js
================================================
/**
* Notes: 预约模块后台管理-控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-08 10:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const AdminMeetService = require('../../service/admin/admin_meet_service.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const MeetModel = require('../../model/meet_model.js');
const contentCheck = require('../../../../framework/validate/content_check.js');
class AdminMeetController extends BaseProjectAdminController {
// 计算可约天数
_getLeaveDay(days) {
let now = timeUtil.time('Y-M-D');
let count = 0;
for (let k = 0; k < days.length; k++) {
if (days[k] >= now) count++;
}
return count;
}
async getDayList() {
await this.isAdmin();
let rules = {
meetId: 'must|id',
start: 'must|date',
end: 'must|date',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
return await service.getDayList(input.meetId, input.start, input.end);
}
/** 生成自助签到码 */
async genSelfCheckinQr() {
await this.isAdmin();
let rules = {
page: 'must|string',
timeMark: 'must|string',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
return await service.genSelfCheckinQr(input.page, input.timeMark);
}
/** 管理员按钮核销 */
async checkinJoin() {
await this.isAdmin();
let rules = {
joinId: 'must|id',
flag: 'must|in:0,1'
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
await service.checkinJoin(input.joinId, input.flag);
}
/** 管理员扫码核验 */
async scanJoin() {
await this.isAdmin();
let rules = {
meetId: 'must|id',
code: 'must|string|len:15',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
await service.scanJoin(input.meetId, input.code);
}
/** 预约排序 */
async sortMeet() { // 数据校验
await this.isAdmin();
let rules = {
meetId: 'must|id',
sort: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
await service.sortMeet(input.meetId, input.sort);
}
/** 首页设定 */
async vouchMeet() {
await this.isAdmin();
let rules = {
id: 'must|id',
vouch: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
await service.vouchMeet(input.id, input.vouch);
}
/** 预约状态修改 */
async statusMeet() {
await this.isAdmin();
// 数据校验
let rules = {
meetId: 'must|id',
status: 'must|int|in:0,1,9,10',
};
// 取得数据
let input = this.validateData(rules);
let title = await MeetModel.getOneField(input.meetId, 'MEET_TITLE');
let service = new AdminMeetService();
await service.statusMeet(input.meetId, input.status);
if (title)
this.logOther('修改了预约《' + title + '》的状态为:' + MeetModel.getDesc('STATUS', input.status));
}
/** 报名状态修改 */
async statusJoin() {
await this.isAdmin();
// 数据校验
let rules = {
joinId: 'must|id',
status: 'must|int|in:0,1,8,9,10,98,99',
reason: 'string|max:200',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
return await service.statusJoin(this._admin, input.joinId, input.status, input.reason);
}
/** 报名删除 */
async delJoin() {
await this.isAdmin();
// 数据校验
let rules = {
joinId: 'must|id'
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
return await service.delJoin(input.joinId);
}
/** 预约项目列表 */
async getAdminMeetList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
whereEx: 'object|name=附加查询条件',
page: 'must|int|default=1',
size: 'int|default=10',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
let result = await service.getAdminMeetList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].MEET_ADD_TIME = timeUtil.timestamp2Time(list[k].MEET_ADD_TIME);
list[k].MEET_EDIT_TIME = timeUtil.timestamp2Time(list[k].MEET_EDIT_TIME);
list[k].leaveDay = this._getLeaveDay(list[k].MEET_DAYS);
}
result.list = list;
return result;
}
/** 预约名单列表 */
async getJoinList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
meetId: 'must|id',
mark: 'must|string',
page: 'must|int|default=1',
size: 'int|default=10',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
let result = await service.getJoinList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].JOIN_EDIT_TIME = timeUtil.timestamp2Time(list[k].JOIN_EDIT_TIME);
//分解成数组,高亮显示
let forms = list[k].JOIN_FORMS;
for (let j in forms) {
forms[j].valArr = dataUtil.splitTextByKey(forms[j].val, input.search);
}
}
result.list = list;
return result;
}
/** 发布 */
async insertMeet() {
await this.isAdmin();
let rules = {
title: 'must|string|min:2|max:50|name=标题',
typeId: 'must|id|name=分类',
typeName: 'must|string|name=分类',
order: 'must|int|min:0|max:9999|name=排序号',
daysSet: 'must|array|name=预约时间设置',
isShowLimit: 'must|int|in:0,1|name=是否显示可预约人数',
formSet: 'must|array|name=用户资料设置',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminMeetService();
let result = await service.insertMeet(this._adminId, input);
this.logOther('创建了新预约《' + input.title + '》');
return result;
}
/** 获取预约信息用于编辑修改 */
async getMeetDetail() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
let detail = await service.getMeetDetail(input.id);
return detail;
}
/** 编辑预约 */
async editMeet() {
await this.isAdmin();
let rules = {
id: 'must|id',
title: 'must|string|min:2|max:50|name=标题',
typeId: 'must|id|name=分类',
typeName: 'must|string|name=分类',
order: 'must|int|min:0|max:9999|name=排序号',
daysSet: 'must|array|name=预约时间设置',
isShowLimit: 'must|int|in:0,1|name=是否显示可预约人数',
formSet: 'must|array|name=用户资料设置',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminMeetService();
let result = service.editMeet(input);
this.logOther('修改了预约《' + input.title + '》');
return result;
}
/** 删除预约 */
async delMeet() {
await this.isAdmin();
// 数据校验
let rules = {
meetId: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let title = await MeetModel.getOneField(input.meetId, 'MEET_TITLE');
let service = new AdminMeetService();
await service.delMeet(input.meetId);
if (title)
this.logOther('删除了预约《' + title + '》');
}
/**
* 更新富文本信息
* @returns 返回 urls数组 [url1, url2, url3, ...]
*/
async updateMeetContent() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
content: 'array'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminMeetService();
return await service.updateMeetContent(input);
}
/**
* 更新封面设置
* @returns 返回 urls数组 [url1, url2, url3, ...]
*/
async updateMeetStyleSet() {
await this.isAdmin();
// 数据校验
let rules = {
meetId: 'must|id',
styleSet: 'object'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminMeetService();
return await service.updateMeetStyleSet(input);
}
// 删除某时段预约记录
async cancelJoinByTimeMark() {
await this.isAdmin();
// 数据校验
let rules = {
meetId: 'must|id',
timeMark: 'must|string',
reason: 'string'
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
return await service.cancelJoinByTimeMark(this._admin, input.meetId, input.timeMark, input.reason);
}
/** 创建模板 */
async insertMeetTemp() {
await this.isAdmin();
let rules = {
name: 'must|string|min:1|max:20|name=名称',
times: 'must|array|name=模板时段',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
let result = await service.insertMeetTemp(input);
return result;
}
/** 编辑模板 */
async editMeetTemp() {
await this.isAdmin();
let rules = {
id: 'must|id',
isLimit: 'must|bool|name=是否限制',
limit: 'must|int|name=人数上限',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
let result = service.editMeetTemp(input);
return result;
}
/** 模板列表 */
async getMeetTempList() {
await this.isAdmin();
let service = new AdminMeetService();
let result = await service.getMeetTempList();
return result;
}
/** 删除模板 */
async delMeetTemp() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
await service.delMeetTemp(input.id);
}
/**************报名数据导出 BEGIN ********************* */
/** 当前是否有导出文件生成 */
async joinDataGet() {
await this.isAdmin();
// 数据校验
let rules = {
isDel: 'int|must', //是否删除已有记录
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
if (input.isDel === 1)
await service.deleteJoinDataExcel(); //先删除
return await service.getJoinDataURL();
}
/** 导出数据 */
async joinDataExport() {
await this.isAdmin();
// 数据校验
let rules = {
meetId: 'id|must',
startDay: 'date|must',
endDay: 'date|must',
status: 'int|must|default=1'
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
return await service.exportJoinDataExcel(input);
}
/** 删除导出的报名数据文件 */
async joinDataDel() {
await this.isAdmin();
// 数据校验
let rules = {};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMeetService();
return await service.deleteJoinDataExcel();
}
}
module.exports = AdminMeetController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_mgr_controller.js
================================================
/**
* Notes: 管理员控制模块
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 10:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const LogModel = require('../../../../framework/platform/model/log_model.js');
const AdminMgrService = require('../../service/admin/admin_mgr_service.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const contentCheck = require('../../../../framework/validate/content_check.js');
class AdminMgrController extends BaseProjectAdminController {
// 管理员登录
async adminLogin() {
// 数据校验
let rules = {
name: 'must|string|min:5|max:30|name=管理员名',
pwd: 'must|string|min:5|max:30|name=密码',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMgrService();
return await service.adminLogin(input.name, input.pwd);
}
/** 删除管理员 */
async delMgr() {
await this.isSuperAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMgrService();
await service.delMgr(input.id, this._adminId);
}
/** 管理员状态修改 */
async statusMgr() {
await this.isSuperAdmin();
// 数据校验
let rules = {
id: 'must|id',
status: 'must|int|in:0,1',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMgrService();
await service.statusMgr(input.id, input.status, this._admin.ADMIN_PHONE);
}
/** 管理员列表 */
async getMgrList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
whereEx: 'object|name=附加查询条件',
page: 'must|int|default=1',
size: 'int|default=10',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMgrService();
let result = await service.getMgrList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].ADMIN_EDIT_TIME = timeUtil.timestamp2Time(list[k].ADMIN_EDIT_TIME);
list[k].ADMIN_LOGIN_TIME = (list[k].ADMIN_LOGIN_TIME == 0) ? '未登录' : timeUtil.timestamp2Time(list[k].ADMIN_LOGIN_TIME);
}
result.list = list;
return result;
}
/** 添加管理员 */
async insertMgr() {
await this.isSuperAdmin();
// 数据校验
let rules = {
name: 'must|string|min:5|max:30|name=账号',
desc: 'must|string|max:30|name=姓名',
phone: 'string|len:11|name=手机',
password: 'must|string|min:6|max:30|name=密码',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminMgrService();
await service.insertMgr(input);
}
/** 修改管理员 */
async editMgr() {
await this.isSuperAdmin();
// 数据校验
let rules = {
id: 'must|id|name=id',
name: 'must|string|min:5|max:30|name=账号',
desc: 'must|string|max:30|name=姓名',
phone: 'string|len:11|name=手机',
password: 'string|min:6|max:30|name=新密码',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminMgrService();
await service.editMgr(input.id, input);
}
/** 修改自己的密码 */
async pwdMgr() {
await this.isAdmin();
// 数据校验
let rules = {
oldPassword: 'must|string|min:6|max:30|name=旧密码',
password: 'must|string|min:6|max:30|name=新密码',
password2: 'must|string|min:6|max:30|name=新密码再次填写',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminMgrService();
await service.pwdtMgr(this._adminId, input.oldPassword, input.password);
}
/** 获取管理员信息用于编辑修改 */
async getMgrDetail() {
await this.isSuperAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMgrService();
return await service.getMgrDetail(input.id);
}
async clearLog() {
await this.isAdmin();
let service = new AdminMgrService();
return await service.clearLog();
}
async getLogList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
whereEx: 'object|name=附加查询条件',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminMgrService();
let result = await service.getLogList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].LOG_TYPE_DESC = LogModel.getDesc('TYPE', list[k].LOG_TYPE);
list[k].LOG_ADD_TIME = timeUtil.timestamp2Time(list[k].LOG_ADD_TIME);
}
result.list = list;
return result;
}
}
module.exports = AdminMgrController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_news_controller.js
================================================
/**
* Notes: 资讯模块后台管理-控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 10:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const AdminNewsService = require('../../service/admin/admin_news_service.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const contentCheck = require('../../../../framework/validate/content_check.js');
const NewsModel = require('../../model/news_model.js');
class AdminNewsController extends BaseProjectAdminController {
/** 置顶与排序设定 */
async sortNews() {
await this.isAdmin();
let rules = {
id: 'must|id',
sort: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminNewsService();
await service.sortNews(input.id, input.sort);
}
/** 首页设定 */
async vouchNews() {
await this.isAdmin();
let rules = {
id: 'must|id',
vouch: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminNewsService();
await service.vouchNews(input.id, input.vouch);
}
/** 资讯状态修改 */
async statusNews() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
status: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminNewsService();
await service.statusNews(input.id, input.status);
}
/** 资讯列表 */
async getAdminNewsList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
whereEx: 'object|name=附加查询条件',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminNewsService();
let result = await service.getAdminNewsList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].NEWS_ADD_TIME = timeUtil.timestamp2Time(list[k].NEWS_ADD_TIME, 'Y-M-D h:m');
list[k].NEWS_EDIT_TIME = timeUtil.timestamp2Time(list[k].NEWS_EDIT_TIME, 'Y-M-D h:m');
if (list[k].NEWS_OBJ && list[k].NEWS_OBJ.desc)
delete list[k].NEWS_OBJ.desc;
}
result.list = list;
return result;
}
/**
* 更新富文本信息
*/
async updateNewsContent() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
content: 'array'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminNewsService();
return await service.updateNewsContent(input);
}
/** 发布资讯信息 */
async insertNews() {
await this.isAdmin();
// 数据校验
let rules = {
title: 'must|string|min:4|max:50|name=标题',
cateId: 'must|string|name=分类',
cateName: 'must|string|name=分类名',
order: 'must|int|min:0|max:9999|name=排序号',
desc: 'must|string|min:10|max:200|name=简介',
forms: 'array|name=表单',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminNewsService();
let result = await service.insertNews(input);
this.logNews('添加了文章《' + input.title + '》');
return result;
}
/** 获取资讯信息用于编辑修改 */
async getNewsDetail() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminNewsService();
return await service.getNewsDetail(input.id);
}
/** 编辑资讯 */
async editNews() {
await this.isAdmin();
let rules = {
id: 'must|id',
title: 'must|string|min:4|max:50|name=标题',
cateId: 'must|string|name=分类',
cateName: 'must|string|name=分类',
order: 'must|int|min:0|max:9999|name=排序号',
desc: 'string|min:10|max:200|name=简介',
forms: 'array|name=表单',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminNewsService();
let result = service.editNews(input);
this.logNews('修改了文章《' + input.title + '》');
return result;
}
/** 删除资讯 */
async delNews() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let title = await NewsModel.getOneField(input.id, 'NEWS_TITLE');
let service = new AdminNewsService();
await service.delNews(input.id);
if (title)
this.logNews('删除了文章《' + title + '》');
}
/**
* 更新图片信息
* @returns 返回 urls数组 [url1, url2, url3, ...]
*/
async updateNewsPic() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
imgList: 'array'
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminNewsService();
return await service.updateNewsPic(input);
}
/** 更新图片信息 */
async updateNewsForms() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
hasImageForms: 'array'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminNewsService();
return await service.updateNewsForms(input);
}
}
module.exports = AdminNewsController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_product_controller.js
================================================
/**
* Notes: 产品模块后台管理-控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 10:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const AdminProductService = require('../../service/admin/admin_product_service.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const contentCheck = require('../../../../framework/validate/content_check.js');
const ProductModel = require('../../model/product_model.js');
class AdminProductController extends BaseProjectAdminController {
/** 置顶与排序设定 */
async sortProduct() {
await this.isAdmin();
let rules = {
id: 'must|id',
sort: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminProductService();
await service.sortProduct(input.id, input.sort);
}
/** 首页设定 */
async vouchProduct() {
await this.isAdmin();
let rules = {
id: 'must|id',
vouch: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminProductService();
await service.vouchProduct(input.id, input.vouch);
}
/** 状态修改 */
async statusProduct() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
status: 'must|int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminProductService();
await service.statusProduct(input.id, input.status);
}
/** 列表 */
async getAdminProductList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
whereEx: 'object|name=附加查询条件',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminProductService();
let result = await service.getAdminProductList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].PRODUCT_ADD_TIME = timeUtil.timestamp2Time(list[k].PRODUCT_ADD_TIME, 'Y-M-D h:m:s');
if (list[k].PRODUCT_OBJ && list[k].PRODUCT_OBJ.desc)
delete list[k].PRODUCT_OBJ.desc;
}
result.list = list;
return result;
}
/** 发布 */
async insertProduct() {
await this.isAdmin();
// 数据校验
let rules = {
title: 'must|string|min:2|max:50|name=标题',
cateId: 'must|string|name=分类',
cateName: 'must|string|name=分类名称',
order: 'must|int|min:0|max:9999|name=排序号',
forms: 'array|name=表单',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminProductService();
let result = await service.insertProduct(input);
this.logOther('添加了《' + input.title + '》');
return result;
}
/** 获取信息用于编辑修改 */
async getProductDetail() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminProductService();
return await service.getProductDetail(input.id);
}
/** 编辑 */
async editProduct() {
await this.isAdmin();
let rules = {
id: 'must|id',
title: 'must|string|min:2|max:50|name=标题',
cateId: 'must|string|name=分类',
cateName: 'must|string|name=分类名称',
order: 'must|int|min:0|max:9999|name=排序号',
forms: 'array|name=表单',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminProductService();
let result = service.editProduct(input);
this.logOther('修改了《' + input.title + '》');
return result;
}
/** 删除 */
async delProduct() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let title = await ProductModel.getOneField(input.id, 'PRODUCT_TITLE');
let service = new AdminProductService();
await service.delProduct(input.id);
if (title)
this.logOther('删除了《' + title + '》');
}
/** 更新图片信息 */
async updateProductForms() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
hasImageForms: 'array'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminProductService();
return await service.updateProductForms(input);
}
}
module.exports = AdminProductController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_setup_controller.js
================================================
/**
* Notes: 设置控制模块
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 10:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const AdminSetupService = require('../../service/admin/admin_setup_service.js');
const contentCheck = require('../../../../framework/validate/content_check.js');
class AdminSetupController extends BaseProjectAdminController {
// 通用setup
async setSetup() {
await this.isAdmin();
// 数据校验
let rules = {
key: 'must|string|name=KEY',
content: 'name=内容',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminSetupService();
await service.setSetup(input.key, input.content);
}
// 富文本setup
async setContentSetup() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|string|name=KEY',
content: 'must|array|name=内容'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiAdmin(input);
let service = new AdminSetupService();
await service.setSetup(input.id, input.content, 'content');
}
async genMiniQr() {
await this.isAdmin();
// 数据校验
let rules = {
path: 'must|string',
sc: 'string',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminSetupService();
return await service.genMiniQr(input.path, input.sc);
}
}
module.exports = AdminSetupController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/admin_user_controller.js
================================================
/**
* Notes: 用户控制模块
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-01-22 10:20:00
*/
const BaseProjectAdminController = require('./base_project_admin_controller.js');
const UserModel = require('../../model/user_model.js');
const AdminUserService = require('../../service/admin/admin_user_service.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
class AdminUserController extends BaseProjectAdminController {
/** 用户信息 */
async getUserDetail() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminUserService();
let user = await service.getUser({
userId: input.id
});
if (user) {
// 显示转换
user.USER_ADD_TIME = timeUtil.timestamp2Time(user.USER_ADD_TIME);
user.USER_LOGIN_TIME = user.USER_LOGIN_TIME ? timeUtil.timestamp2Time(user.USER_LOGIN_TIME) : '未登录';
}
return user;
}
/** 用户列表 */
async getUserList() {
await this.isAdmin();
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
whereEx: 'object|name=附加查询条件',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminUserService();
let result = await service.getUserList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].USER_STATUS_DESC = UserModel.getDesc('STATUS', list[k].USER_STATUS);
list[k].USER_ADD_TIME = timeUtil.timestamp2Time(list[k].USER_ADD_TIME);
list[k].USER_LOGIN_TIME = list[k].USER_LOGIN_TIME ? timeUtil.timestamp2Time(list[k].USER_LOGIN_TIME) : '未登录';
}
result.list = list;
return result;
}
/** 删除用户 */
async delUser() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let title = await UserModel.getOneField({ USER_MINI_OPENID: input.id }, 'USER_NAME');
let service = new AdminUserService();
await service.delUser(input.id);
if (title)
this.logUser('删除了用户「' + title + '」');
}
async statusUser() {
await this.isAdmin();
// 数据校验
let rules = {
id: 'must|id',
status: 'must|int',
reason: 'string'
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminUserService();
await service.statusUser(input.id, input.status, input.reason);
}
/************** 用户数据导出 BEGIN ********************* */
/** 当前是否有导出文件生成 */
async userDataGet() {
await this.isAdmin();
// 数据校验
let rules = {
isDel: 'int|must', //是否删除已有记录
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminUserService();
if (input.isDel === 1)
await service.deleteUserDataExcel(); //先删除
return await service.getUserDataURL();
}
/** 导出数据 */
async userDataExport() {
await this.isAdmin();
// 数据校验
let rules = {
condition: 'string|name=导出条件',
fields: 'array',
};
// 取得数据
let input = this.validateData(rules);
let service = new AdminUserService();
return await service.exportUserDataExcel(input.condition, input.fields);
}
/** 删除导出的用户数据 */
async userDataDel() {
await this.isAdmin();
// 数据校验
let rules = {};
// 取得数据
let input = this.validateData(rules);
let service = new AdminUserService();
return await service.deleteUserDataExcel();
}
}
module.exports = AdminUserController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/admin/base_project_admin_controller.js
================================================
/**
* Notes: 后台管理控制模块
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-03-15 19:20:00
*/
const BaseAdminController = require('../../../../framework/platform/controller/base_admin_controller.js');
const BaseProjectService = require('../../service/base_project_service.js');
class BaseProjectAdminController extends BaseAdminController {
// TODO
async initSetup() {
let service = new BaseProjectService();
await service.initSetup();
}
}
module.exports = BaseProjectAdminController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/album_controller.js
================================================
/**
* Notes: 相册模块控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-07 04:00:00
*/
const BaseProjectController = require('./base_project_controller.js');
const AlbumService = require('../service/album_service.js');
const timeUtil = require('../../../framework/utils/time_util.js');
class AlbumController extends BaseProjectController {
/** 列表 */
async getAlbumList() {
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new AlbumService();
let result = await service.getAlbumList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].ALBUM_ADD_TIME = timeUtil.timestamp2Time(list[k].ALBUM_ADD_TIME, 'Y-M-D');
if (list[k].ALBUM_OBJ && list[k].ALBUM_OBJ.detail)
delete list[k].ALBUM_OBJ.detail;
}
return result;
}
/** 浏览详细 */
async viewAlbum() {
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new AlbumService();
let album = await service.viewAlbum(input.id);
if (album) {
// 显示转换
album.ALBUM_ADD_TIME = timeUtil.timestamp2Time(album.ALBUM_ADD_TIME, 'Y-M-D');
}
return album;
}
}
module.exports = AlbumController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/base_project_controller.js
================================================
/**
* Notes: 本业务基本控制器
* Date: 2021-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseController = require('../../../framework/platform/controller/base_controller.js');
const BaseProjectService = require('../service/base_project_service.js');
class BaseProjectController extends BaseController {
// TODO
async initSetup() {
let service = new BaseProjectService();
await service.initSetup();
}
}
module.exports = BaseProjectController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/check_controller.js
================================================
/**
* Notes: 内容检测控制器
* Date: 2021-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseProjectController = require('./base_project_controller.js');
const contentCheck = require('../../framework/validate/content_check.js');
class CheckController extends BaseProjectController {
/**
* 图片校验
*/
async checkImg() {
// 数据校验
let rules = {
img: 'name=img',
mine: 'must|default=jpg',
};
// 取得数据
let input = this.validateData(rules);
return await contentCheck.checkImg(input.img, 'jpg');
}
}
module.exports = CheckController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/fav_controller.js
================================================
/**
* Notes: 预约模块控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-10 04:00:00
*/
const BaseProjectController = require('./base_project_controller.js');
const FavService = require('../service/fav_service.js');
const timeUtil = require('../../../framework/utils/time_util.js');
class FavController extends BaseProjectController {
/** 更新某人收藏 */
async updateFav() {
// 数据校验
let rules = {
oid: 'id|must',
title: 'string|must',
type: 'string|must',
path: 'string|must',
};
// 取得数据
let input = this.validateData(rules);
let service = new FavService();
return await service.updateFav(this._userId, input.oid, input.title, input.type, input.path);
}
/** 删除收藏 */
async delFav() {
// 数据校验
let rules = {
oid: 'id|must'
};
// 取得数据
let input = this.validateData(rules);
let service = new FavService();
return await service.updateFav(this._userId, input.oid);
}
/** 是否收藏 */
async isFav() {
// 数据校验
let rules = {
oid: 'id|must',
};
// 取得数据
let input = this.validateData(rules);
let service = new FavService();
return await service.isFav(this._userId, input.oid);
}
/** 我的收藏列表 */
async getMyFavList() {
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new FavService();
let result = await service.getMyFavList(this._userId, input);
// 数据格式化
let list = result.list;
// 显示转换
for (let k = 0; k < list.length; k++) {
list[k].FAV_ADD_TIME = timeUtil.timestamp2Time(list[k].FAV_ADD_TIME);
}
result.list = list;
return result;
}
}
module.exports = FavController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/home_controller.js
================================================
/**
* Notes: 全局或者主页模块控制器
* Date: 2020-11-05 10:20:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseProjectController = require('./base_project_controller.js');
const HomeService = require('../service/home_service.js');
class HomeController extends BaseProjectController {
async getSetup() {
// 数据校验
let rules = {
key: 'must|string|name=KEY',
};
// 取得数据
let input = this.validateData(rules);
let service = new HomeService();
return await service.getSetup(input.key);
}
/** 首页推荐列表 */
async getHomeList() {
let service = new HomeService();
return await service.getHomeList();
}
}
module.exports = HomeController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/meet_controller.js
================================================
/**
* Notes: 预约模块控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-10 04:00:00
*/
const BaseProjectController = require('./base_project_controller.js');
const MeetService = require('../service/meet_service.js');
const timeUtil = require('../../../framework/utils/time_util.js');
const JoinModel = require('../model/join_model.js');
class MeetController extends BaseProjectController {
// 把列表转换为显示模式
transMeetList(list) {
let ret = [];
for (let k = 0; k < list.length; k++) {
let node = {};
node.type = 'meet';
node.id = list[k]._id;
node.title = list[k].MEET_TITLE;
node.desc = list[k].MEET_STYLE_SET.desc;
node.ext = list[k].openRule;
node.pic = list[k].MEET_STYLE_SET.pic;
ret.push(node);
}
return ret;
}
/** 按天获取预约项目 */
async getMeetListByDay() {
// 数据校验
let rules = {
day: 'must|date|name=日期',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let list = await service.getMeetListByDay(input.day);
return list;
}
/** 获取从某天开始可预约的日期 */
async getHasDaysFromDay() {
// 数据校验
let rules = {
day: 'must|date|name=日期',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let list = await service.getHasDaysFromDay(input.day);
return list;
}
/** 预约列表 */
async getMeetList() {
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
typeId: 'string',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let result = await service.getMeetList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].openRule = this._getLeaveDay(list[k].MEET_DAYS) + '天可预约';
}
result.list = this.transMeetList(list);
return result;
}
/** 我的预约列表 */
async getMyJoinList() {
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let result = await service.getMyJoinList(this._userId, input);
// 数据格式化
let list = result.list;
let now = timeUtil.time('Y-M-D h:m');
for (let k = 0; k < list.length; k++) {
if (now > (list[k].JOIN_MEET_DAY + ' ' + list[k].JOIN_MEET_TIME_END))
list[k].isTimeout = 1;
else
list[k].isTimeout = 0;
list[k].JOIN_MEET_DAY = timeUtil.fmtDateCHN(list[k].JOIN_MEET_DAY) + ' (' + timeUtil.week(list[k].JOIN_MEET_DAY) + ')';
list[k].JOIN_ADD_TIME = timeUtil.timestamp2Time(list[k].JOIN_ADD_TIME, 'Y-M-D h:m');
}
result.list = list;
return result;
}
/** 我的某日预约列表 */
async getMyJoinSomeday() {
// 数据校验
let rules = {
day: 'must|date|name=日期',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let list = await service.getMyJoinSomeday(this._userId, input.day);
// 数据格式化
for (let k = 0; k < list.length; k++) {
}
return list;
}
/** 我的预约详情 */
async getMyJoinDetail() {
// 数据校验
let rules = {
joinId: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let join = await service.getMyJoinDetail(this._userId, input.joinId);
if (join) {
join.JOIN_STATUS_DESC = JoinModel.getDesc('STATUS', join.JOIN_STATUS);
join.JOIN_ADD_TIME = timeUtil.timestamp2Time(join.JOIN_ADD_TIME);
}
return join;
}
/** 用户预约取消 */
async cancelMyJoin() {
// 数据校验
let rules = {
joinId: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
return await service.cancelMyJoin(this._userId, input.joinId);
}
/** 用户自助签到 */
async userSelfCheckin() {
// 数据校验
let rules = {
timeMark: 'must|string',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
return await service.userSelfCheckin(this._userId, input.timeMark);
}
/** 预约前获取关键信息 */
async detailForJoin() {
// 数据校验
let rules = {
meetId: 'must|meetId',
timeMark: 'must|string',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let meet = await service.detailForJoin(this._userId, input.meetId, input.timeMark);
if (meet) {
// 显示转换
}
return meet;
}
/** 浏览预约信息 */
async viewMeet() {
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
let meet = await service.viewMeet(input.id);
if (meet) {
// 显示转换
}
return meet;
}
/** 预约前检测 */
async beforeJoin() {
// 数据校验
let rules = {
meetId: 'must|id',
timeMark: 'must|string',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
return await service.beforeJoin(this._userId, input.meetId, input.timeMark);
}
/** 预约提交 */
async join() {
// 数据校验
let rules = {
meetId: 'must|id',
timeMark: 'must|string',
forms: 'must|array',
};
// 取得数据
let input = this.validateData(rules);
let service = new MeetService();
return await service.join(this._userId, input.meetId, input.timeMark, input.forms);
}
// 计算可约天数
_getLeaveDay(days) {
let now = timeUtil.time('Y-M-D');
let count = 0;
for (let k = 0; k < days.length; k++) {
if (days[k] >= now) count++;
}
return count;
}
}
module.exports = MeetController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/my_controller.js
================================================
/**
* Notes: 用户中心模块控制器
* Date: 2021-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseProjectController = require('./base_project_controller.js');
class MyController extends BaseProjectController {
}
module.exports = MyController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/news_controller.js
================================================
/**
* Notes: 资讯模块控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-29 04:00:00
*/
const BaseProjectController = require('./base_project_controller.js');
const NewsService = require('../service/news_service.js');
const timeUtil = require('../../../framework/utils/time_util.js');
class NewsController extends BaseProjectController {
// 把列表转换为显示模式
transNewsList(list) {
let ret = [];
for (let k = 0; k < list.length; k++) {
let node = {};
node.type = 'news';
node.id = list[k]._id;
node.title = list[k].NEWS_TITLE;
node.desc = list[k].NEWS_DESC;
node.ext = list[k].NEWS_ADD_TIME;
node.pic = list[k].NEWS_PIC[0];
ret.push(node);
}
return ret;
}
/** 资讯列表 */
async getNewsList() {
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
cateId: 'string',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new NewsService();
let result = await service.getNewsList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].NEWS_ADD_TIME = timeUtil.timestamp2Time(list[k].NEWS_ADD_TIME, 'Y-M-D');
if (list[k].NEWS_OBJ && list[k].NEWS_OBJ.desc)
delete list[k].NEWS_OBJ.desc;
}
result.list = this.transNewsList(list);
return result;
}
/** 浏览资讯信息 */
async viewNews() {
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new NewsService();
let news = await service.viewNews(input.id);
if (news) {
// 显示转换
news.NEWS_ADD_TIME = timeUtil.timestamp2Time(news.NEWS_ADD_TIME, 'Y-M-D');
}
return news;
}
}
module.exports = NewsController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/passport_controller.js
================================================
/**
* Notes: passport模块控制器
* Date: 2021-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseProjectController = require('./base_project_controller.js');
const PassportService = require('../service/passport_service.js');
const contentCheck = require('../../../framework/validate/content_check.js');
class PassportController extends BaseProjectController {
/** 取得我的用户信息 */
async getMyDetail() {
let service = new PassportService();
return await service.getMyDetail(this._userId);
}
/** 获取手机号码 */
async getPhone() {
// 数据校验
let rules = {
cloudID: 'must|string|min:1|max:200|name=cloudID',
};
// 取得数据
let input = this.validateData(rules);
let service = new PassportService();
return await service.getPhone(input.cloudID);
}
/** 注册 */
async register() {
// 数据校验
let rules = {
name: 'must|string|min:1|max:30|name=昵称',
mobile: 'must|mobile|name=手机',
forms: 'array|name=表单',
status: 'int|default=1'
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiClient(input);
let service = new PassportService();
return await service.register(this._userId, input);
}
/** 修改用户资料 */
async editBase() {
// 数据校验
let rules = {
name: 'must|string|min:1|max:30|name=昵称',
mobile: 'must|mobile|name=手机',
forms: 'array|name=表单',
};
// 取得数据
let input = this.validateData(rules);
// 内容审核
await contentCheck.checkTextMultiClient(input);
let service = new PassportService();
return await service.editBase(this._userId, input);
}
/** 登录 */
async login() {
// 数据校验
let rules = {};
// 取得数据
let input = this.validateData(rules);
let service = new PassportService();
return await service.login(this._userId);
}
}
module.exports = PassportController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/product_controller.js
================================================
/**
* Notes: 产品模块控制器
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-08 04:00:00
*/
const BaseProjectController = require('./base_project_controller.js');
const ProductService = require('../service/product_service.js');
const timeUtil = require('../../../framework/utils/time_util.js');
const dataUtil = require('../../../framework/utils/data_util.js');
class ProductController extends BaseProjectController {
/** 列表 */
async getProductList() {
// 数据校验
let rules = {
search: 'string|min:1|max:30|name=搜索条件',
sortType: 'string|name=搜索类型',
sortVal: 'name=搜索类型值',
orderBy: 'object|name=排序',
page: 'must|int|default=1',
size: 'int',
isTotal: 'bool',
oldTotal: 'int',
};
// 取得数据
let input = this.validateData(rules);
let service = new ProductService();
let result = await service.getProductList(input);
// 数据格式化
let list = result.list;
for (let k = 0; k < list.length; k++) {
list[k].PRODUCT_ADD_TIME = timeUtil.timestamp2Time(list[k].PRODUCT_ADD_TIME, 'Y-M-D');
list[k].PRODUCT_OBJ.desc = dataUtil.fmtText(list[k].PRODUCT_OBJ.desc, 100);
list[k].PRODUCT_OBJ.star = (list[k].PRODUCT_OBJ.star);
}
return result;
}
/** 浏览详细 */
async viewProduct() {
// 数据校验
let rules = {
id: 'must|id',
};
// 取得数据
let input = this.validateData(rules);
let service = new ProductService();
let product = await service.viewProduct(input.id);
if (product) {
// 显示转换
product.PRODUCT_ADD_TIME = timeUtil.timestamp2Time(product.PRODUCT_ADD_TIME, 'Y-M-D');
}
return product;
}
}
module.exports = ProductController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/controller/test/test_controller.js
================================================
/**
* Notes: 测试模块控制器
* Date: 2021-03-15 19:20:00
*/
const BaseController = require('../../controller/base_project_controller.js');
const fakerLib = require('../../../../framework/lib/faker_lib.js');
const VoteModel = require('../../model/vote_model.js');
const VoteJoinModel = require('../../model/vote_join_model.js');
const VoteService = require('../../service/vote_service.js');
const EnrollModel = require('../../model/enroll_model.js');
const EnrollJoinModel = require('../../model/enroll_join_model.js');
const ActivityModel = require('../../model/activity_model.js');
const ActivityJoinModel = require('../../model/activity_join_model.js');
class TestController extends BaseController {
async test() {
console.log('TEST>>>>>>>');
//this.mockEnrollJoin();
//this.mockActivityJoin();
this.mockVoteJoin();
}
async mockEnrollJoin() {
console.log('mockEnrollJoin >>>>>>> Begin....');
let ENROLL_ID = 'b69f67c062d7d46b0bc4d2985c084775';
let enroll = await EnrollModel.getOne(ENROLL_ID);
if (!enroll) return console.error('no enroll');
let join = {};
join.ENROLL_JOIN_ENROLL_ID = ENROLL_ID;
join.ENROLL_JOIN_STATUS = 1;
console.log('>>>>delete');
let delCnt = await EnrollJoinModel.del({ ENROLL_JOIN_ENROLL_ID: ENROLL_ID });
console.log('>>>>delete=' + delCnt);
for (let k = 1; k <= 10; k++) {
console.log('>>>>insert >' + k);
join.ENROLL_JOIN_USER_ID = fakerLib.getUuid();
join.ENROLL_JOIN_LAST_TIME = fakerLib.getAddTimestamp();
join.ENROLL_JOIN_ADD_TIME = fakerLib.getAddTimestamp();
join.ENROLL_JOIN_FORMS = [
{ mark: 'name', title: '姓名', type: 'text', val: fakerLib.getName() },
{ mark: 'phone', title: '手机', type: 'mobile', val: fakerLib.getMobile() }
];
await EnrollJoinModel.insert(join);
}
console.log('>>>> STAT');
let cnt = await EnrollJoinModel.count({
ENROLL_JOIN_ENROLL_ID: ENROLL_ID
});
await EnrollModel.edit(ENROLL_ID, { ENROLL_JOIN_CNT: cnt, ENROLL_MAX_CNT: cnt + 10 });
console.log('mockEnrollJoin >>>>>>> END');
}
async mockActivityJoin() {
console.log('mockActivityJoin >>>>>>> Begin....');
let ACTIVITY_ID = '0a4ec1f962d867481158b94f6441f613';
let activity = await ActivityModel.getOne(ACTIVITY_ID);
if (!activity) return console.error('no activity');
let join = {};
join.ACTIVITY_JOIN_ACTIVITY_ID = ACTIVITY_ID;
join.ACTIVITY_JOIN_STATUS = 1;
console.log('>>>>delete');
let delCnt = await ActivityJoinModel.del({ ACTIVITY_JOIN_ACTIVITY_ID: ACTIVITY_ID });
console.log('>>>>delete=' + delCnt);
for (let k = 1; k <= 10; k++) {
console.log('>>>>insert >' + k);
join.ACTIVITY_JOIN_USER_ID = fakerLib.getUuid();
join.ACTIVITY_JOIN_CODE = fakerLib.getStr(15);
join.ACTIVITY_JOIN_ADD_TIME = fakerLib.getAddTimestamp();
join.ACTIVITY_JOIN_FORMS = [
{ mark: 'name', title: '姓名', type: 'text', val: fakerLib.getName() },
{ mark: 'phone', title: '手机', type: 'mobile', val: fakerLib.getMobile() }
];
await ActivityJoinModel.insert(join);
}
console.log('>>>> STAT');
let cnt = await ActivityJoinModel.count({
ACTIVITY_JOIN_ACTIVITY_ID: ACTIVITY_ID
});
await ActivityModel.edit(ACTIVITY_ID, { ACTIVITY_JOIN_CNT: cnt, ACTIVITY_MAX_CNT: cnt + 10 });
console.log('mockActivityJoin >>>>>>> END');
}
async mockVoteJoin() {
console.log('mockVoteJoin >>>>>>> Begin....');
let VOTE_ID = 'b69f67c062c3a2b209da6ef133c9d874';
let vote = await VoteModel.getOne(VOTE_ID);
if (!vote) return console.error('no vote');
let join = {};
join.VOTE_JOIN_VOTE_ID = VOTE_ID;
console.log('>>>>delete');
let delCnt = await VoteJoinModel.del({ VOTE_JOIN_VOTE_ID: VOTE_ID });
console.log('>>>>delete=' + delCnt);
for (let k = 1; k <= 10; k++) {
console.log('>>>>insert >' + k);
join.VOTE_JOIN_USER_ID = fakerLib.getUuid();
join.VOTE_JOIN_ADD_TIME = fakerLib.getAddTimestamp();
join.VOTE_JOIN_CNT = 1;
join.VOTE_JOIN_SELECTED = [k % vote.VOTE_ITEM.length];
await VoteJoinModel.insert(join);
}
console.log('>>>> STAT');
let service = new VoteService();
service.statVoteItem(VOTE_ID);
console.log('mockVoteJoin >>>>>>> END');
}
}
module.exports = TestController;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/album_model.js
================================================
/**
* Notes: 相册实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-05 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class AlbumModel extends BaseProjectModel {
}
// 集合名
AlbumModel.CL = BaseProjectModel.C('album');
AlbumModel.DB_STRUCTURE = {
_pid: 'string|true',
ALBUM_ID: 'string|true',
ALBUM_TITLE: 'string|true|comment=标题',
ALBUM_STATUS: 'int|true|default=1|comment=状态 0=未启用,1=使用中',
ALBUM_CATE_ID: 'string|true|default=0|comment=分类',
ALBUM_CATE_NAME: 'string|false|comment=分类名冗余',
ALBUM_ORDER: 'int|true|default=9999',
ALBUM_VOUCH: 'int|true|default=0',
ALBUM_FORMS: 'array|true|default=[]',
ALBUM_OBJ: 'object|true|default={}',
ALBUM_QR: 'string|false',
ALBUM_VIEW_CNT: 'int|true|default=0',
ALBUM_ADD_TIME: 'int|true',
ALBUM_EDIT_TIME: 'int|true',
ALBUM_ADD_IP: 'string|false',
ALBUM_EDIT_IP: 'string|false',
};
// 字段前缀
AlbumModel.FIELD_PREFIX = "ALBUM_";
/**
* 状态 0=未启用,1=使用中
*/
AlbumModel.STATUS = {
UNUSE: 0,
COMM: 1
};
module.exports = AlbumModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/base_project_model.js
================================================
/**
* Notes: 实体ORM基类
* Date: 2022-03-15 19:20:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseModel = require('../../../framework/platform/model/base_model.js');
class BaseProjectModel extends BaseModel {
}
module.exports = BaseProjectModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/day_model.js
================================================
/**
* Notes: 预约日期设置实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-01-25 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class DayModel extends BaseProjectModel {
}
// 集合名
DayModel.CL = BaseProjectModel.C('day');
DayModel.DB_STRUCTURE = {
_pid: 'string|true',
DAY_ID: 'string|true',
DAY_MEET_ID: 'string|true',
day: 'string|true|comment=日期 yyyy-mm-dd',
dayDesc: 'string|true|comment=描述',
times: 'array|true|comment=具体时间段',
/*
{
1. mark=唯一性标识,
2. start=开始时间点hh:mm ~,
3. end=结束时间点hh:mm,
4. isLimit=是否人数限制,
5. limit=报名上限,
6. status=状态 0/1
7. stat:{ //统计数据
succCnt=1预约成功*,
cancelCnt=10已取消,
adminCancelCnt=99后台取消
}
}',
*/
DAY_ADD_TIME: 'int|true',
DAY_EDIT_TIME: 'int|true',
DAY_ADD_IP: 'string|false',
DAY_EDIT_IP: 'string|false',
};
// 字段前缀
DayModel.FIELD_PREFIX = "DAY_";
module.exports = DayModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/fav_model.js
================================================
/**
* Notes: 收藏实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-05-24 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class FavModel extends BaseProjectModel {
}
// 集合名
FavModel.CL = BaseProjectModel.C('fav');
FavModel.DB_STRUCTURE = {
_pid: 'string|true',
FAV_ID: 'string|true',
FAV_USER_ID: 'string|true',
FAV_TITLE: 'string|true|comment=标题',
FAV_TYPE: 'string|true|comment=类型',
FAV_OID: 'string|true|comment=相关表主键',
FAV_PATH: 'string|true|comment=路径',
FAV_ADD_TIME: 'int|true',
FAV_EDIT_TIME: 'int|true',
FAV_ADD_IP: 'string|false',
FAV_EDIT_IP: 'string|false',
};
// 字段前缀
FavModel.FIELD_PREFIX = "FAV_";
module.exports = FavModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/join_model.js
================================================
/**
* Notes: 报名实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-30 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class JoinModel extends BaseProjectModel {
}
// 集合名
JoinModel.CL = BaseProjectModel.C('join');
JoinModel.DB_STRUCTURE = {
_pid: 'string|true',
JOIN_ID: 'string|true',
JOIN_EDIT_ADMIN_ID: 'string|false|comment=最近修改的管理员ID',
JOIN_EDIT_ADMIN_NAME: 'string|false|comment=最近修改的管理员名',
JOIN_EDIT_ADMIN_TIME: 'int|true|default=0|comment=管理员最近修改的时间',
JOIN_EDIT_ADMIN_STATUS: 'int|false|comment=最近管理员修改为的状态 ',
JOIN_IS_ADMIN: 'int|true|default=0|comment=是否管理员添加 0/1',
JOIN_CODE: 'string|true|comment=核验码15位',
JOIN_IS_CHECKIN: 'int|true|default=0|comment=是否签到 0/1 ',
JOIN_USER_ID: 'string|true|comment=用户ID',
JOIN_MEET_ID: 'string|true|comment=预约PK',
JOIN_MEET_TITLE: 'string|true|comment=项目',
JOIN_MEET_DAY: 'string|true|comment=日期',
JOIN_MEET_TIME_START: 'string|true|comment=时段开始',
JOIN_MEET_TIME_END: 'string|true|comment=时段结束',
JOIN_MEET_TIME_MARK: 'string|true|comment=时段标识',
JOIN_START_TIME: 'int|true|comment=开始时间戳',
JOIN_FORMS: 'array|true|default=[]|comment=表单',
/* title:
mark:
type:
val:
*/
JOIN_STATUS: 'int|true|default=1|comment=状态 1=预约成功,10=已取消, 99=系统取消',
JOIN_REASON: 'string|false|comment=审核拒绝或者取消理由',
JOIN_ADD_TIME: 'int|true',
JOIN_EDIT_TIME: 'int|true',
JOIN_ADD_IP: 'string|false',
JOIN_EDIT_IP: 'string|false',
};
// 字段前缀
JoinModel.FIELD_PREFIX = "JOIN_";
/**
* 状态 1=预约成功,10=已取消, 99=后台取消
*/
JoinModel.STATUS = {
SUCC: 1,
CANCEL: 10,
ADMIN_CANCEL: 99
};
JoinModel.STATUS_DESC = {
SUCC: '预约成功',
CANCEL: '已取消',
ADMIN_CANCEL: '系统取消',
};
module.exports = JoinModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/meet_model.js
================================================
/**
* Notes: 预约实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-12-07 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class MeetModel extends BaseProjectModel {
}
// 集合名
MeetModel.CL = BaseProjectModel.C('meet');
MeetModel.DB_STRUCTURE = {
_pid: 'string|true',
MEET_ID: 'string|true',
MEET_ADMIN_ID: 'string|true|comment=添加的管理员',
MEET_TITLE: 'string|true|comment=标题',
MEET_CONTENT: 'array|true|default=[]|comment=详细介绍',
MEET_DAYS: 'array|true|default=[]|comment=最近一次修改保存的可用日期',
MEET_TYPE_ID: 'string|true|comment=分类编号',
MEET_TYPE_NAME: 'string|true|comment=分类冗余',
MEET_IS_SHOW_LIMIT: 'int|true|default=1|comment=是否显示可预约人数',
MEET_STYLE_SET: 'object|true|default={}|comment=样式设置',
MEET_FORM_SET: 'array|true|default=[]|comment=表单字段设置',
MEET_STATUS: 'int|true|default=1|comment=状态 0=未启用,1=使用中,9=停止预约,10=已关闭',
MEET_ORDER: 'int|true|default=9999',
MEET_VOUCH: 'int|true|default=0',
MEET_QR: 'string|false',
MEET_ADD_TIME: 'int|true',
MEET_EDIT_TIME: 'int|true',
MEET_ADD_IP: 'string|false',
MEET_EDIT_IP: 'string|false',
};
// 字段前缀
MeetModel.FIELD_PREFIX = "MEET_";
/**
* 状态 0=未启用,1=使用中,9=停止预约,10=已关闭
*/
MeetModel.STATUS = {
UNUSE: 0,
COMM: 1,
OVER: 9,
CLOSE: 10
};
MeetModel.STATUS_DESC = {
UNUSE: '未启用',
COMM: '使用中',
OVER: '停止预约(可见)',
CLOSE: '已关闭(不可见)'
};
module.exports = MeetModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/news_model.js
================================================
/**
* Notes: 资讯实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-10-28 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class NewsModel extends BaseProjectModel {
}
// 集合名
NewsModel.CL = BaseProjectModel.C('news');
NewsModel.DB_STRUCTURE = {
_pid: 'string|true',
NEWS_ID: 'string|true',
NEWS_TITLE: 'string|false|comment=标题',
NEWS_DESC: 'string|false|comment=描述',
NEWS_STATUS: 'int|true|default=1|comment=状态 0/1',
NEWS_CATE_ID: 'string|true|comment=分类编号',
NEWS_CATE_NAME: 'string|true|comment=分类冗余',
NEWS_ORDER: 'int|true|default=9999',
NEWS_VOUCH: 'int|true|default=0',
NEWS_CONTENT: 'array|true|default=[]|comment=内容',
NEWS_QR: 'string|false',
NEWS_VIEW_CNT: 'int|true|default=0|comment=访问次数',
NEWS_PIC: 'array|false|default=[]|comment=封面图 [cloudId1,cloudId2,cloudId3...]',
NEWS_FORMS: 'array|true|default=[]',
NEWS_OBJ: 'object|true|default={}',
NEWS_ADD_TIME: 'int|true',
NEWS_EDIT_TIME: 'int|true',
NEWS_ADD_IP: 'string|false',
NEWS_EDIT_IP: 'string|false',
};
// 字段前缀
NewsModel.FIELD_PREFIX = "NEWS_";
module.exports = NewsModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/product_model.js
================================================
/**
* Notes: 套系实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-08 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class ProductModel extends BaseProjectModel {
}
// 集合名
ProductModel.CL = BaseProjectModel.C('product');
ProductModel.DB_STRUCTURE = {
_pid: 'string|true',
PRODUCT_ID: 'string|true',
PRODUCT_TITLE: 'string|true|comment=标题',
PRODUCT_STATUS: 'int|true|default=1|comment=状态 0=未启用,1=使用中',
PRODUCT_CATE_ID: 'string|true|default=0|comment=分类',
PRODUCT_CATE_NAME: 'string|false|comment=分类冗余',
PRODUCT_ORDER: 'int|true|default=9999',
PRODUCT_VOUCH: 'int|true|default=0',
PRODUCT_FORMS: 'array|true|default=[]',
PRODUCT_OBJ: 'object|true|default={}',
PRODUCT_QR: 'string|false',
PRODUCT_VIEW_CNT: 'int|true|default=0',
PRODUCT_ADD_TIME: 'int|true',
PRODUCT_EDIT_TIME: 'int|true',
PRODUCT_ADD_IP: 'string|false',
PRODUCT_EDIT_IP: 'string|false',
};
// 字段前缀
ProductModel.FIELD_PREFIX = "PRODUCT_";
/**
* 状态 0=未启用,1=使用中
*/
ProductModel.STATUS = {
UNUSE: 0,
COMM: 1
};
module.exports = ProductModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/model/user_model.js
================================================
/**
* Notes: 用户实体
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-10-14 19:20:00
*/
const BaseProjectModel = require('./base_project_model.js');
class UserModel extends BaseProjectModel { }
// 集合名
UserModel.CL = BaseProjectModel.C('user');
UserModel.DB_STRUCTURE = {
_pid: 'string|true',
USER_ID: 'string|true',
USER_MINI_OPENID: 'string|true|comment=小程序openid',
USER_STATUS: 'int|true|default=1|comment=状态 0=待审核,1=正常,8=审核未过,9=禁用',
USER_CHECK_REASON: 'string|false|comment=审核未过的理由',
USER_NAME: 'string|false|comment=用户昵称',
USER_MOBILE: 'string|false|comment=联系电话',
USER_FORMS: 'array|true|default=[]',
USER_OBJ: 'object|true|default={}',
USER_LOGIN_CNT: 'int|true|default=0|comment=登陆次数',
USER_LOGIN_TIME: 'int|false|comment=最近登录时间',
USER_ADD_TIME: 'int|true',
USER_ADD_IP: 'string|false',
USER_EDIT_TIME: 'int|true',
USER_EDIT_IP: 'string|false',
}
// 字段前缀
UserModel.FIELD_PREFIX = "USER_";
/**
* 状态 0=待审核,1=正常,2=审核未过,9=禁用
*/
UserModel.STATUS = {
UNUSE: 0,
COMM: 1,
UNCHECK: 8,
FORBID: 9
};
UserModel.STATUS_DESC = {
UNUSE: '待审核',
COMM: '正常',
UNCHECK: '未通过审核',
FORBID: '禁用'
};
module.exports = UserModel;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/public/constants.js
================================================
module.exports = {
// 首页推荐
SETUP_HOME_VOUCH_KEY : 'SETUP_HOME_VOUCH_KEY',
}
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/public/project_config.js
================================================
module.exports = {
// ## 缓存相关
CACHE_CALENDAR_TIME: 60 * 30, //日历缓存
}
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/public/route.js
================================================
/**
* Notes: 路由配置文件
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* User: CC
* Date: 2020-10-14 07:00:00
*/
module.exports = {
'test/test': 'test/test_controller@test',
'home/setup_get': 'home_controller@getSetup',
'passport/login': 'passport_controller@login',
'passport/phone': 'passport_controller@getPhone',
'passport/my_detail': 'passport_controller@getMyDetail',
'passport/register': 'passport_controller@register',
'passport/edit_base': 'passport_controller@editBase',
// 收藏
'fav/update': 'fav_controller@updateFav',
'fav/del': 'fav_controller@delFav',
'fav/is_fav': 'fav_controller@isFav',
'fav/my_list': 'fav_controller@getMyFavList',
'admin/home': 'admin/admin_home_controller@adminHome',
'admin/clear_vouch': 'admin/admin_home_controller@clearVouchData',
'admin/login': 'admin/admin_mgr_controller@adminLogin',
'admin/mgr_list': 'admin/admin_mgr_controller@getMgrList',
'admin/mgr_insert': 'admin/admin_mgr_controller@insertMgr#demo',
'admin/mgr_del': 'admin/admin_mgr_controller@delMgr#demo',
'admin/mgr_detail': 'admin/admin_mgr_controller@getMgrDetail',
'admin/mgr_edit': 'admin/admin_mgr_controller@editMgr#demo',
'admin/mgr_status': 'admin/admin_mgr_controller@statusMgr#demo',
'admin/mgr_pwd': 'admin/admin_mgr_controller@pwdMgr#demo',
'admin/log_list': 'admin/admin_mgr_controller@getLogList',
'admin/log_clear': 'admin/admin_mgr_controller@clearLog#demo',
'admin/setup_set': 'admin/admin_setup_controller@setSetup#demo',
'admin/setup_set_content': 'admin/admin_setup_controller@setContentSetup#demo',
'admin/setup_qr': 'admin/admin_setup_controller@genMiniQr',
// 用户
'admin/user_list': 'admin/admin_user_controller@getUserList',
'admin/user_detail': 'admin/admin_user_controller@getUserDetail',
'admin/user_del': 'admin/admin_user_controller@delUser#demo',
'admin/user_status': 'admin/admin_user_controller@statusUser#demo',
'admin/user_data_get': 'admin/admin_user_controller@userDataGet',
'admin/user_data_export': 'admin/admin_user_controller@userDataExport',
'admin/user_data_del': 'admin/admin_user_controller@userDataDel',
// 内容
'home/list': 'home_controller@getHomeList',
'news/list': 'news_controller@getNewsList',
'news/view': 'news_controller@viewNews',
'admin/news_list': 'admin/admin_news_controller@getAdminNewsList',
'admin/news_insert': 'admin/admin_news_controller@insertNews#demo',
'admin/news_detail': 'admin/admin_news_controller@getNewsDetail',
'admin/news_edit': 'admin/admin_news_controller@editNews#demo',
'admin/news_update_forms': 'admin/admin_news_controller@updateNewsForms#demo',
'admin/news_update_pic': 'admin/admin_news_controller@updateNewsPic#demo',
'admin/news_update_content': 'admin/admin_news_controller@updateNewsContent#demo',
'admin/news_del': 'admin/admin_news_controller@delNews#demo',
'admin/news_sort': 'admin/admin_news_controller@sortNews#demo',
'admin/news_status': 'admin/admin_news_controller@statusNews#demo',
'admin/news_vouch': 'admin/admin_news_controller@vouchNews#demo',
// 相册
'album/list': 'album_controller@getAlbumList',
'album/view': 'album_controller@viewAlbum',
'admin/album_list': 'admin/admin_album_controller@getAdminAlbumList',
'admin/album_insert': 'admin/admin_album_controller@insertAlbum#demo',
'admin/album_detail': 'admin/admin_album_controller@getAlbumDetail',
'admin/album_edit': 'admin/admin_album_controller@editAlbum#demo#demo',
'admin/album_update_forms': 'admin/admin_album_controller@updateAlbumForms#demo',
'admin/album_del': 'admin/admin_album_controller@delAlbum#demo',
'admin/album_sort': 'admin/admin_album_controller@sortAlbum#demo',
'admin/album_vouch': 'admin/admin_album_controller@vouchAlbum#demo',
'admin/album_status': 'admin/admin_album_controller@statusAlbum#demo',
// 产品
'product/list': 'product_controller@getProductList',
'product/view': 'product_controller@viewProduct',
'admin/product_list': 'admin/admin_product_controller@getAdminProductList',
'admin/product_insert': 'admin/admin_product_controller@insertProduct#demo',
'admin/product_detail': 'admin/admin_product_controller@getProductDetail',
'admin/product_edit': 'admin/admin_product_controller@editProduct#demo',
'admin/product_update_forms': 'admin/admin_product_controller@updateProductForms#demo',
'admin/product_del': 'admin/admin_product_controller@delProduct#demo',
'admin/product_sort': 'admin/admin_product_controller@sortProduct#demo',
'admin/product_vouch': 'admin/admin_product_controller@vouchProduct#demo',
'admin/product_status': 'admin/admin_product_controller@statusProduct#demo',
// 预约
'meet/list': 'meet_controller@getMeetList',
'meet/list_by_day': 'meet_controller@getMeetListByDay',
'meet/list_has_day': 'meet_controller@getHasDaysFromDay',
'meet/view': 'meet_controller@viewMeet',
'meet/detail_for_join': 'meet_controller@detailForJoin',
'meet/before_join': 'meet_controller@beforeJoin',
'meet/join': 'meet_controller@join',
'meet/my_join_list': 'meet_controller@getMyJoinList',
'meet/my_join_cancel': 'meet_controller@cancelMyJoin',
'meet/my_join_detail': 'meet_controller@getMyJoinDetail',
'meet/my_join_someday': 'meet_controller@getMyJoinSomeday',
'meet/my_join_checkin': 'meet_controller@userSelfCheckin',
'admin/meet_list': 'admin/admin_meet_controller@getAdminMeetList',
'admin/meet_join_list': 'admin/admin_meet_controller@getJoinList',
'admin/join_status': 'admin/admin_meet_controller@statusJoin#demo',
'admin/join_del': 'admin/admin_meet_controller@delJoin#demo',
'admin/meet_insert': 'admin/admin_meet_controller@insertMeet#demo',
'admin/meet_detail': 'admin/admin_meet_controller@getMeetDetail',
'admin/meet_edit': 'admin/admin_meet_controller@editMeet#demo',
'admin/meet_del': 'admin/admin_meet_controller@delMeet#demo',
'admin/meet_update_content': 'admin/admin_meet_controller@updateMeetContent#demo',
'admin/meet_update_style': 'admin/admin_meet_controller@updateMeetStyleSet#demo',
'admin/meet_sort': 'admin/admin_meet_controller@sortMeet#demo',
'admin/meet_vouch': 'admin/admin_meet_controller@vouchMeet#demo',
'admin/meet_status': 'admin/admin_meet_controller@statusMeet#demo',
'admin/meet_cancel_time_join': 'admin/admin_meet_controller@cancelJoinByTimeMark#demo',
'admin/join_scan': 'admin/admin_meet_controller@scanJoin',
'admin/join_checkin': 'admin/admin_meet_controller@checkinJoin',
'admin/self_checkin_qr': 'admin/admin_meet_controller@genSelfCheckinQr',
'admin/meet_day_list': 'admin/admin_meet_controller@getDayList',
'admin/meet_temp_insert': 'admin/admin_meet_controller@insertMeetTemp#demo',
'admin/meet_temp_list': 'admin/admin_meet_controller@getMeetTempList',
'admin/meet_temp_del': 'admin/admin_meet_controller@delMeetTemp#demo',
'admin/meet_temp_edit': 'admin/admin_meet_controller@editMeetTemp#demo',
'admin/join_data_get': 'admin/admin_meet_controller@joinDataGet',
'admin/join_data_export': 'admin/admin_meet_controller@joinDataExport',
'admin/join_data_del': 'admin/admin_meet_controller@joinDataDel',
}
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_album_service.js
================================================
/**
* Notes: 相册后台管理
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 07:48:00
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const util = require('../../../../framework/utils/util.js');
const cloudUtil = require('../../../../framework/cloud/cloud_util.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const AlbumModel = require('../../model/album_model.js');
const AdminHomeService = require('../admin/admin_home_service.js');
class AdminAlbumService extends BaseProjectAdminService {
/** 推荐首页SETUP */
async vouchAlbumSetup(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**添加 */
async insertAlbum({
title,
cateId,
cateName,
order,
forms
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**删除数据 */
async delAlbum(id) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**获取信息 */
async getAlbumDetail(id) {
let fields = '*';
let where = {
_id: id
}
let album = await AlbumModel.getOne(where, fields);
if (!album) return null;
return album;
}
// 更新forms信息
async updateAlbumForms({
id,
hasImageForms
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**更新数据 */
async editAlbum({
id,
title,
cateId, // 二级分类
cateName,
order,
forms,
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**取得分页列表 */
async getAdminAlbumList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
whereEx, //附加查询条件
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ALBUM_ORDER': 'asc',
'ALBUM_ADD_TIME': 'desc'
};
let fields = 'ALBUM_TITLE,ALBUM_CATE_ID,ALBUM_CATE_NAME,ALBUM_EDIT_TIME,ALBUM_ADD_TIME,ALBUM_ORDER,ALBUM_STATUS,ALBUM_VOUCH,ALBUM_QR,ALBUM_OBJ';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
if (util.isDefined(search) && search) {
where.or = [
{ ALBUM_TITLE: ['like', search] },
];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'cateId': {
where.and.ALBUM_CATE_ID = String(sortVal);
break;
}
case 'status': {
where.and.ALBUM_STATUS = Number(sortVal);
break;
}
case 'vouch': {
where.and.ALBUM_VOUCH = 1;
break;
}
case 'top': {
where.and.ALBUM_ORDER = 0;
break;
}
case 'sort': {
orderBy = this.fmtOrderBySort(sortVal, 'ALBUM_ADD_TIME');
break;
}
}
}
return await AlbumModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
/**修改状态 */
async statusAlbum(id, status) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**置顶与排序设定 */
async sortAlbum(id, sort) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**首页设定 */
async vouchAlbum(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
}
module.exports = AdminAlbumService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_home_service.js
================================================
/**
* Notes: 后台HOME/登录模块
* Date: 2021-06-15 07:48:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const UserModel = require('../../model/user_model.js');
const MeetModel = require('../../model/meet_model.js');
const NewsModel = require('../../model/news_model.js');
const ProductModel = require('../../model/product_model.js');
const AlbumModel = require('../../model/album_model.js');
const constants = require('../../public/constants.js');
const setupUtil = require('../../../../framework/utils/setup/setup_util.js');
class AdminHomeService extends BaseProjectAdminService {
/**
* 首页数据归集
*/
async adminHome() {
let where = {};
let userCnt = await UserModel.count(where);
let newsCnt = await NewsModel.count(where);
let meetCnt = await MeetModel.count(where);
let productCnt = await ProductModel.count(where);
let albumCnt = await AlbumModel.count(where);
return [
{ title: '用户数', cnt: userCnt },
{ title: '资讯数', cnt: newsCnt },
{ title: '预约项目', cnt: meetCnt },
{ title: '景点数', cnt: productCnt },
{ title: '攻略数', cnt: albumCnt },
]
}
// 用户数据清理
async clearUserData(userId) {
}
//##################首页推荐
// 首页推荐清理
async clearVouchData() {
await setupUtil.remove(constants.SETUP_HOME_VOUCH_KEY);
NewsModel.edit({}, { NEWS_VOUCH: 0 });
MeetModel.edit({}, { MEET_VOUCH: 0 });
AlbumModel.edit({}, { ALBUM_VOUCH: 0 });
ProductModel.edit({}, { PRODUCT_VOUCH: 0 });
}
/**添加首页推荐 */
async updateHomeVouch(node) {
if (node.ext) node.ext = '#' + node.ext;
let key = constants.SETUP_HOME_VOUCH_KEY;
let list = await setupUtil.get(key);
if (!list || !Array.isArray(list)) list = [];
// 重复性判断
for (let k = 0; k < list.length; k++) {
if (list[k].id == node.id) {
// 已存在
list[k] = node;
return await setupUtil.set(key, list, 'vouch');
}
}
// 赋值
let data = node;
list.unshift(data);
await setupUtil.set(key, list, 'vouch');
}
/**删除推荐数据 */
async delHomeVouch(id) {
let key = constants.SETUP_HOME_VOUCH_KEY;
let list = await setupUtil.get(key);
if (!list || !Array.isArray(list)) return;
let newList = [];
for (let k = 0; k < list.length; k++) {
if (list[k].id != id) {
newList.push(list[k]);
}
}
return await setupUtil.set(key, newList, 'vouch');
}
}
module.exports = AdminHomeService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_meet_service.js
================================================
/**
* Notes: 预约后台管理
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-08 07:48:00
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const MeetService = require('../meet_service.js');
const AdminHomeService = require('../admin/admin_home_service.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const setupUtil = require('../../../../framework/utils/setup/setup_util.js');
const util = require('../../../../framework/utils/util.js');
const cloudUtil = require('../../../../framework/cloud/cloud_util.js');
const cloudBase = require('../../../../framework/cloud/cloud_base.js');
const MeetModel = require('../../model/meet_model.js');
const JoinModel = require('../../model/join_model.js');
const DayModel = require('../../model/day_model.js');
const exportUtil = require('../../../../framework/utils/export_util.js');
const SETUP_MEET_TEMP_KEY = 'SETUP_MEET_TEMP';
// 导出报名数据KEY
const EXPORT_JOIN_DATA_KEY = 'EXPORT_JOIN_DATA';
class AdminMeetService extends BaseProjectAdminService {
/** 推荐首页SETUP */
async vouchMeetSetup(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 预约数据列表 */
async getDayList(meetId, start, end) {
let where = {
DAY_MEET_ID: meetId,
day: ['between', start, end]
}
let orderBy = {
day: 'asc'
}
return await DayModel.getAllBig(where, 'day,times,dayDesc', orderBy);
}
// 按项目统计人数
async statJoinCntByMeet(meetId) {
let today = timeUtil.time('Y-M-D');
let where = {
day: ['>=', today],
DAY_MEET_ID: meetId
}
let meetService = new MeetService();
let list = await DayModel.getAllBig(where, 'DAY_MEET_ID,times', {}, 1000);
for (let k = 0; k < list.length; k++) {
let meetId = list[k].DAY_MEET_ID;
let times = list[k].times;
for (let j in times) {
let timeMark = times[j].mark;
meetService.statJoinCnt(meetId, timeMark);
}
}
}
/** 自助签到码 */
async genSelfCheckinQr(page, timeMark) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 管理员按钮核销 */
async checkinJoin(joinId, flag) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 管理员扫码核销 */
async scanJoin(meetId, code) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**
* 判断本日是否有预约记录
* @param {*} daySet daysSet的节点
*/
checkHasJoinCnt(times) {
if (!times) return false;
for (let k = 0; k < times.length; k++) {
if (times[k].stat.succCnt) return true;
}
return false;
}
// 判断含有预约的日期
getCanModifyDaysSet(daysSet) {
let now = timeUtil.time('Y-M-D');
for (let k = 0; k < daysSet.length; k++) {
if (daysSet[k].day < now) continue;
daysSet[k].hasJoin = this.checkHasJoinCnt(daysSet[k].times);
}
return daysSet;
}
/** 取消某个时间段的所有预约记录 */
async cancelJoinByTimeMark(admin, meetId, timeMark, reason) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**添加 */
async insertMeet(adminId, {
title,
order,
typeId,
typeName,
daysSet,
isShowLimit,
formSet,
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**删除数据 */
async delMeet(id) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**获取信息 */
async getMeetDetail(id) {
let fields = '*';
let where = {
_id: id
}
let meet = await MeetModel.getOne(where, fields);
if (!meet) return null;
let meetService = new MeetService();
meet.MEET_DAYS_SET = await meetService.getDaysSet(id, timeUtil.time('Y-M-D')); //今天及以后
return meet;
}
/**
* 更新富文本详细的内容及图片信息
* @returns 返回 urls数组 [url1, url2, url3, ...]
*/
async updateMeetContent({
id,
content // 富文本数组
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**
* 更新封面内容及图片信息
* @returns 返回 urls数组 [url1, url2, url3, ...]
*/
async updateMeetStyleSet({
meetId,
styleSet
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 更新日期设置 */
async _editDays(meetId, nowDay, daysSetData) {
// 删除指定日期之后的记录(含)
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**更新数据 */
async editMeet({
id,
title,
typeId,
typeName,
order,
daysSet,
isShowLimit,
formSet
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**预约名单分页列表 */
async getJoinList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
meetId,
mark,
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'JOIN_EDIT_TIME': 'desc'
};
let fields = 'JOIN_IS_CHECKIN,JOIN_CODE,JOIN_ID,JOIN_REASON,JOIN_USER_ID,JOIN_MEET_ID,JOIN_MEET_TITLE,JOIN_MEET_DAY,JOIN_MEET_TIME_START,JOIN_MEET_TIME_END,JOIN_MEET_TIME_MARK,JOIN_FORMS,JOIN_STATUS,JOIN_EDIT_TIME';
let where = {
JOIN_MEET_ID: meetId,
JOIN_MEET_TIME_MARK: mark
};
if (util.isDefined(search) && search) {
where['JOIN_FORMS.val'] = {
$regex: '.*' + search,
$options: 'i'
};
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'status':
// 按类型
sortVal = Number(sortVal);
if (sortVal == 1099) //取消的2种
where.JOIN_STATUS = ['in', [10, 99]]
else
where.JOIN_STATUS = Number(sortVal);
break;
case 'checkin':
// 签到
where.JOIN_STATUS = JoinModel.STATUS.SUCC;
if (sortVal == 1) {
where.JOIN_IS_CHECKIN = 1;
} else {
where.JOIN_IS_CHECKIN = 0;
}
break;
}
}
return await JoinModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
/**预约项目分页列表 */
async getAdminMeetList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
whereEx, //附加查询条件
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'MEET_ORDER': 'asc',
'MEET_ADD_TIME': 'desc'
};
let fields = 'MEET_TYPE,MEET_TYPE_NAME,MEET_TITLE,MEET_STATUS,MEET_DAYS,MEET_ADD_TIME,MEET_EDIT_TIME,MEET_ORDER,MEET_VOUCH,MEET_QR';
let where = {};
if (util.isDefined(search) && search) {
where.MEET_TITLE = {
$regex: '.*' + search,
$options: 'i'
};
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'status':
// 按类型
where.MEET_STATUS = Number(sortVal);
break;
case 'typeId':
// 按类型
where.MEET_TYPE_ID = sortVal;
break;
case 'sort':
// 排序
if (sortVal == 'view') {
orderBy = {
'MEET_VIEW_CNT': 'desc',
'MEET_ADD_TIME': 'desc'
};
}
break;
}
}
return await MeetModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
/** 删除 */
async delJoin(joinId) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**修改报名状态
* 特殊约定 99=>正常取消
*/
async statusJoin(admin, joinId, status, reason = '') {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**修改项目状态 */
async statusMeet(id, status) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**置顶排序设定 */
async sortMeet(id, sort) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**首页设定 */
async vouchMeet(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
//##################模板
/**添加模板 */
async insertMeetTemp({
name,
times,
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**更新数据 */
async editMeetTemp({
id,
limit,
isLimit
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**删除数据 */
async delMeetTemp(id) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**模板列表 */
async getMeetTempList() {
let list = await setupUtil.get(SETUP_MEET_TEMP_KEY);
if (!list || !Array.isArray(list)) list = [];
return list;
}
// #####################导出报名数据
/**获取报名数据 */
async getJoinDataURL() {
return await exportUtil.getExportDataURL(EXPORT_JOIN_DATA_KEY);
}
/**删除报名数据 */
async deleteJoinDataExcel() {
return await exportUtil.deleteDataExcel(EXPORT_JOIN_DATA_KEY);
}
/**导出报名数据 */
async exportJoinDataExcel({
meetId,
startDay,
endDay,
status
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
}
module.exports = AdminMeetService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_mgr_service.js
================================================
/**
* Notes: 管理员管理
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 07:48:00
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const util = require('../../../../framework/utils/util.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const AdminModel = require('../../../../framework/platform/model/admin_model.js');
const LogModel = require('../../../../framework/platform/model/log_model.js');
const md5Lib = require('../../../../framework/lib/md5_lib.js');
class AdminMgrService extends BaseProjectAdminService {
//**管理员登录 */
async adminLogin(name, password) {
// 判断是否存在
let where = {
ADMIN_STATUS: 1,
ADMIN_NAME: name,
ADMIN_PASSWORD: md5Lib.md5(password)
}
let fields = 'ADMIN_ID,ADMIN_NAME,ADMIN_DESC,ADMIN_TYPE,ADMIN_LOGIN_TIME,ADMIN_LOGIN_CNT';
let admin = await AdminModel.getOne(where, fields);
if (!admin)
this.AppError('管理员不存在或者已停用');
let cnt = admin.ADMIN_LOGIN_CNT;
// 生成token
let token = dataUtil.genRandomString(32);
let tokenTime = timeUtil.time();
let data = {
ADMIN_TOKEN: token,
ADMIN_TOKEN_TIME: tokenTime,
ADMIN_LOGIN_TIME: timeUtil.time(),
ADMIN_LOGIN_CNT: cnt + 1
}
await AdminModel.edit(where, data);
let type = admin.ADMIN_TYPE;
let last = (!admin.ADMIN_LOGIN_TIME) ? '尚未登录' : timeUtil.timestamp2Time(admin.ADMIN_LOGIN_TIME);
// 写日志
this.insertLog('登录了系统', admin, LogModel.TYPE.SYS);
return {
token,
name: admin.ADMIN_NAME,
type,
last,
cnt
}
}
async clearLog() {
let where = {}
await LogModel.del(where);
}
/** 取得日志分页列表 */
async getLogList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
whereEx, //附加查询条件
page,
size,
oldTotal = 0
}) {
orderBy = orderBy || {
LOG_ADD_TIME: 'desc'
};
let fields = '*';
let where = {};
if (util.isDefined(search) && search) {
where.or = [{
LOG_CONTENT: ['like', search]
}, {
LOG_ADMIN_DESC: ['like', search]
}, {
LOG_ADMIN_NAME: ['like', search]
}];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'type':
// 按类型
where.LOG_TYPE = Number(sortVal);
break;
}
}
let result = await LogModel.getList(where, fields, orderBy, page, size, true, oldTotal);
return result;
}
/** 获取所有管理员 */
async getMgrList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
whereEx, //附加查询条件
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = {
ADMIN_ADD_TIME: 'desc'
}
let fields = 'ADMIN_NAME,ADMIN_STATUS,ADMIN_PHONE,ADMIN_TYPE,ADMIN_LOGIN_CNT,ADMIN_LOGIN_TIME,ADMIN_DESC,ADMIN_EDIT_TIME,ADMIN_EDIT_IP';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
if (util.isDefined(search) && search) {
where.or = [{
ADMIN_NAME: ['like', search]
},
{
ADMIN_PHONE: ['like', search]
},
{
ADMIN_DESC: ['like', search]
}
];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'status':
// 按类型
where.and.ADMIN_STATUS = Number(sortVal);
break;
case 'type':
// 按类型
where.and.ADMIN_TYPE = Number(sortVal);
break;
}
}
return await AdminModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
/** 删除管理员 */
async delMgr(id, myAdminId) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 添加新的管理员 */
async insertMgr({
name,
desc,
phone,
password
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 修改状态 */
async statusMgr(id, status, myAdminId) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 获取管理员信息 */
async getMgrDetail(id) {
let fields = '*';
let where = {
_id: id
}
let mgr = await AdminModel.getOne(where, fields);
if (!mgr) return null;
return mgr;
}
/** 修改管理员 */
async editMgr(id, {
name,
desc,
phone,
password
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/** 修改自身密码 */
async pwdtMgr(adminId, oldPassword, password) {
let where = {
_id: adminId,
ADMIN_PASSWORD: md5Lib.md5(oldPassword),
}
let admin = await AdminModel.getOne(where);
if (!admin)
this.AppError('旧密码错误');
let data = {
ADMIN_PASSWORD: md5Lib.md5(password),
}
return await AdminModel.edit(adminId, data);
}
}
module.exports = AdminMgrService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_news_service.js
================================================
/**
* Notes: 资讯后台管理
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 07:48:00
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const AdminHomeService = require('../admin/admin_home_service.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const util = require('../../../../framework/utils/util.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const cloudUtil = require('../../../../framework/cloud/cloud_util.js');
const NewsModel = require('../../model/news_model.js');
class AdminNewsService extends BaseProjectAdminService {
/** 推荐首页SETUP */
async vouchNewsSetup(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**添加资讯 */
async insertNews({
title,
cateId, //分类
cateName,
order,
desc = '',
forms
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**删除资讯数据 */
async delNews(id) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**获取资讯信息 */
async getNewsDetail(id) {
let fields = '*';
let where = {
_id: id
}
let news = await NewsModel.getOne(where, fields);
if (!news) return null;
return news;
}
// 更新forms信息
async updateNewsForms({
id,
hasImageForms
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**
* 更新富文本详细的内容及图片信息
* @returns 返回 urls数组 [url1, url2, url3, ...]
*/
async updateNewsContent({
id,
content // 富文本数组
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**
* 更新资讯图片信息
* @returns 返回 urls数组 [url1, url2, url3, ...]
*/
async updateNewsPic({
id,
imgList // 图片数组
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**更新资讯数据 */
async editNews({
id,
title,
cateId, //分类
cateName,
order,
desc = '',
forms
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**取得资讯分页列表 */
async getAdminNewsList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
whereEx, //附加查询条件
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'NEWS_ORDER': 'asc',
'NEWS_ADD_TIME': 'desc'
};
let fields = 'NEWS_TITLE,NEWS_DESC,NEWS_CATE_ID,NEWS_CATE_NAME,NEWS_EDIT_TIME,NEWS_ADD_TIME,NEWS_ORDER,NEWS_STATUS,NEWS_CATE2_NAME,NEWS_VOUCH,NEWS_QR,NEWS_OBJ';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
if (util.isDefined(search) && search) {
where.or = [
{ NEWS_TITLE: ['like', search] },
];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'cateId': {
where.and.NEWS_CATE_ID = String(sortVal);
break;
}
case 'status': {
where.and.NEWS_STATUS = Number(sortVal);
break;
}
case 'vouch': {
where.and.NEWS_VOUCH = 1;
break;
}
case 'top': {
where.and.NEWS_ORDER = 0;
break;
}
case 'sort': {
orderBy = this.fmtOrderBySort(sortVal, 'NEWS_ADD_TIME');
break;
}
}
}
return await NewsModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
/**修改资讯状态 */
async statusNews(id, status) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**置顶与排序设定 */
async sortNews(id, sort) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**首页设定 */
async vouchNews(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
}
module.exports = AdminNewsService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_product_service.js
================================================
/**
* Notes: 产品后台管理
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-08 07:48:00
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const AdminHomeService = require('../admin/admin_home_service.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const util = require('../../../../framework/utils/util.js');
const cloudUtil = require('../../../../framework/cloud/cloud_util.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const ProductModel = require('../../model/product_model.js');
class AdminProductService extends BaseProjectAdminService {
/** 推荐首页SETUP */
async vouchProductSetup(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**添加 */
async insertProduct({
title,
cateId,
cateName,
order,
forms
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**删除数据 */
async delProduct(id) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**获取信息 */
async getProductDetail(id) {
let fields = '*';
let where = {
_id: id
}
let product = await ProductModel.getOne(where, fields);
if (!product) return null;
return product;
}
// 更新forms信息
async updateProductForms({
id,
hasImageForms
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**更新数据 */
async editProduct({
id,
title,
cateId, // 二级分类
cateName,
order,
forms,
}) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**取得分页列表 */
async getAdminProductList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
whereEx, //附加查询条件
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'PRODUCT_ORDER': 'asc',
'PRODUCT_ADD_TIME': 'desc'
};
let fields = 'PRODUCT_TITLE,PRODUCT_CATE_ID,PRODUCT_CATE_NAME,PRODUCT_EDIT_TIME,PRODUCT_ADD_TIME,PRODUCT_ORDER,PRODUCT_STATUS,PRODUCT_VOUCH,,PRODUCT_QR,PRODUCT_OBJ';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
if (util.isDefined(search) && search) {
where.or = [
{ PRODUCT_TITLE: ['like', search] },
];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'cateId': {
where.and.PRODUCT_CATE_ID = String(sortVal);
break;
}
case 'status': {
where.and.PRODUCT_STATUS = Number(sortVal);
break
}
case 'vouch': {
where.and.PRODUCT_VOUCH = 1;
break;
}
case 'top': {
where.and.PRODUCT_ORDER = 0;
break;
}
case 'sort': {
orderBy = this.fmtOrderBySort(sortVal, 'PRODUCT_ADD_TIME');
break;
}
}
}
return await ProductModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
/**修改状态 */
async statusProduct(id, status) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**置顶与排序设定 */
async sortProduct(id, sort) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**首页设定 */
async vouchProduct(id, vouch) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
}
module.exports = AdminProductService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_setup_service.js
================================================
/**
* Notes: 设置管理
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-07-11 07:48:00
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const cloudBase = require('../../../../framework/cloud/cloud_base.js');
const cloudUtil = require('../../../../framework/cloud/cloud_util.js');
const setupUtil = require('../../../../framework/utils/setup/setup_util.js');
const config = require('../../../../config/config.js');
const md5Lib = require('../../../../framework/lib/md5_lib.js');
class AdminSetupService extends BaseProjectAdminService {
// 通用setup
async setSetup(key, val, type = '') {
await setupUtil.set(key, val, type);
}
/** 小程序码 */
async genMiniQr(page, sc = 'qr') {
//生成小程序qr buffer
let cloud = cloudBase.getCloud();
if (page.startsWith('/')) page = page.substring(1);
console.log('page=' + page, ', scene=' + sc);
let color = ['0', '0', '0'];
if (config.TEST_MODE == true && page.includes('default/index/default_index')) {
// 首页且测试模式
let rd = PID;
rd = rd.match(/\d+/g).join('');
rd = Number(rd) % 20;
let colorArr = [];
colorArr.push('0 238 238');
colorArr.push('47 79 79');
colorArr.push('105 139 105');
colorArr.push('119 136 153');
colorArr.push('100 149 237');
colorArr.push('0 205 0');
colorArr.push('176 196 222');
colorArr.push('205 190 112');
colorArr.push('255 20 147');
colorArr.push('139 90 0');
colorArr.push('205 16 118');
colorArr.push('255 174 185');
colorArr.push('108 166 205');
colorArr.push('0 0 139');
colorArr.push('130 130 130');
colorArr.push('205 150 205');
colorArr.push('205 102 0');
colorArr.push('139 101 8');
colorArr.push('72 209 204');
colorArr.push('176 196 222');
color = colorArr[rd].split(' ');
}
let result = await cloud.openapi.wxacode.getUnlimited({
scene: sc,
width: 280,
lineColor: {
r: color[0],
g: color[1],
b: color[2],
},
check_path: false,
//env_version: 'trial', //release,trial,develop
page
});
let cloudPath = PID + '/' + 'setup/' + md5Lib.md5(page) + '.png';
let upload = await cloud.uploadFile({
cloudPath,
fileContent: result.buffer,
});
if (!upload || !upload.fileID) return;
let ret = await cloudUtil.getTempFileURLOne(upload.fileID);
return ret + '?rd=' + this._timestamp;
}
}
module.exports = AdminSetupService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/admin_user_service.js
================================================
/**
* Notes: 用户管理
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-01-22 07:48:00
*/
const BaseProjectAdminService = require('./base_project_admin_service.js');
const util = require('../../../../framework/utils/util.js');
const exportUtil = require('../../../../framework/utils/export_util.js');
const timeUtil = require('../../../../framework/utils/time_util.js');
const dataUtil = require('../../../../framework/utils/data_util.js');
const UserModel = require('../../model/user_model.js');
const AdminHomeService = require('./admin_home_service.js');
// 导出用户数据KEY
const EXPORT_USER_DATA_KEY = 'EXPORT_USER_DATA';
class AdminUserService extends BaseProjectAdminService {
/** 获得某个用户信息 */
async getUser({
userId,
fields = '*'
}) {
let where = {
USER_MINI_OPENID: userId,
}
return await UserModel.getOne(where, fields);
}
/** 取得用户分页列表 */
async getUserList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
whereEx, //附加查询条件
page,
size,
oldTotal = 0
}) {
orderBy = orderBy || {
USER_ADD_TIME: 'desc'
};
let fields = '*';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
if (util.isDefined(search) && search) {
where.or = [{
USER_NAME: ['like', search]
},
{
USER_MOBILE: ['like', search]
},
{
USER_MEMO: ['like', search]
},
];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'status':
where.and.USER_STATUS = Number(sortVal);
break;
case 'sort':
// 排序
if (sortVal == 'newdesc') { //最新
orderBy = {
'USER_ADD_TIME': 'desc'
};
}
if (sortVal == 'newasc') {
orderBy = {
'USER_ADD_TIME': 'asc'
};
}
}
}
let result = await UserModel.getList(where, fields, orderBy, page, size, true, oldTotal, false);
// 为导出增加一个参数condition
result.condition = encodeURIComponent(JSON.stringify(where));
return result;
}
async statusUser(id, status, reason) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
/**删除用户 */
async delUser(id) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
// #####################导出用户数据
/**获取用户数据 */
async getUserDataURL() {
return await exportUtil.getExportDataURL(EXPORT_USER_DATA_KEY);
}
/**删除用户数据 */
async deleteUserDataExcel() {
return await exportUtil.deleteDataExcel(EXPORT_USER_DATA_KEY);
}
/**导出用户数据 */
async exportUserDataExcel(condition, fields) {
this.AppError('该功能暂不开放,如有需要请加作者微信:cclinux0730');
}
}
module.exports = AdminUserService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/admin/base_project_admin_service.js
================================================
/**
* Notes: 后台管理模块 基类
* Date: 2021-03-15 07:48:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseAdminService = require('../../../../framework/platform/service/base_admin_service.js');
;
const util = require('../../../../framework/utils/util.js');
const cloudBase = require('../../../../framework/cloud/cloud_base.js');
class BaseProjectAdminService extends BaseAdminService {
getProjectId() {
return util.getProjectId();
}
async genDetailQr(type, id) {
let cloud = cloudBase.getCloud();
let page = `projects/${this.getProjectId()}/pages/${type}/detail/${type}_detail`;
console.log('page=', page);
let result = await cloud.openapi.wxacode.getUnlimited({
scene: id,
width: 280,
check_path: false,
//env_version: 'trial', //release,trial,develop
page
});
let cloudPath = `${this.getProjectId()}/${type}/${id}/qr.png`;
console.log('cloudPath=', cloudPath);
let upload = await cloud.uploadFile({
cloudPath,
fileContent: result.buffer,
});
if (!upload || !upload.fileID) return;
return upload.fileID;
}
}
module.exports = BaseProjectAdminService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/album_service.js
================================================
/**
* Notes: 相册模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-07 07:48:00
*/
const BaseProjectService = require('./base_project_service.js');
const util = require('../../../framework/utils/util.js');
const AlbumModel = require('../model/album_model.js');
class AlbumService extends BaseProjectService {
/** 浏览资讯信息 */
async viewAlbum(id) {
let fields = '*';
let where = {
_id: id,
ALBUM_STATUS: AlbumModel.STATUS.COMM
}
let album = await AlbumModel.getOne(where, fields);
if (!album) return null;
AlbumModel.inc(id, 'ALBUM_VIEW_CNT', 1);
return album;
}
/** 取得分页列表 */
async getAlbumList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'ALBUM_ORDER': 'asc',
'ALBUM_ADD_TIME': 'desc'
};
let fields = 'ALBUM_OBJ,ALBUM_VIEW_CNT,ALBUM_TITLE,ALBUM_CATE_ID,ALBUM_ADD_TIME,ALBUM_ORDER,ALBUM_STATUS,ALBUM_CATE_NAME';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
where.and.ALBUM_STATUS = AlbumModel.STATUS.COMM; // 状态
if (util.isDefined(search) && search) {
where.or = [
{ ALBUM_TITLE: ['like', search] },
];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'cateId': {
if (sortVal) where.and.ALBUM_CATE_ID = String(sortVal);
break;
}
case 'sort': {
orderBy = this.fmtOrderBySort(sortVal, 'ALBUM_ADD_TIME');
break;
}
}
}
return await AlbumModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
}
module.exports = AlbumService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/base_project_service.js
================================================
/**
* Notes: 业务基类
* Date: 2021-03-15 04:00:00
*/
const dbUtil = require('../../../framework/database/db_util.js');
const util = require('../../../framework/utils/util.js');
const AdminModel = require('../../../framework/platform/model/admin_model.js');
const NewsModel = require('../model/news_model.js');
const MeetModel = require('../model/meet_model.js');
const AlbumModel = require('../model/album_model.js');
const ProductModel = require('../model/product_model.js');
const BaseService = require('../../../framework/platform/service/base_service.js');
class BaseProjectService extends BaseService {
getProjectId() {
return util.getProjectId();
}
async initSetup() {
let F = (c) => 'bx_' + c;
const INSTALL_CL = 'setup_trip1';
const COLLECTIONS = ['setup', 'admin', 'day', 'join', 'log', 'meet', 'news', 'product', 'album', 'fav', 'user'];
const CONST_PIC = '/images/cover.gif';
const NEWS_CATE = '1=本景区动态,2=美食,3=特产';
const MEET_TYPE = '1=景点预约,2=停车预约';
const ALBUM_CATE = '1=线路,2=吃喝,3=住宿,4=购物,5=其他';
const PRODUCT_CATE = '1=仙山贡水,2=伍家台,3=狮子关,4=其他景点';
if (await dbUtil.isExistCollection(F(INSTALL_CL))) {
return;
}
console.log('### initSetup...');
let arr = COLLECTIONS;
for (let k = 0; k < arr.length; k++) {
if (!await dbUtil.isExistCollection(F(arr[k]))) {
await dbUtil.createCollection(F(arr[k]));
}
}
if (await dbUtil.isExistCollection(F('admin'))) {
let adminCnt = await AdminModel.count({});
if (adminCnt == 0) {
let data = {};
data.ADMIN_NAME = 'admin';
data.ADMIN_PASSWORD = 'e10adc3949ba59abbe56e057f20f883e';
data.ADMIN_DESC = '超管';
data.ADMIN_TYPE = 1;
await AdminModel.insert(data);
}
}
if (await dbUtil.isExistCollection(F('news'))) {
let newsCnt = await NewsModel.count({});
if (newsCnt == 0) {
let newsArr = NEWS_CATE.split(',');
for (let j in newsArr) {
let title = newsArr[j].split('=')[1];
let cateId = newsArr[j].split('=')[0];
let data = {};
data.NEWS_TITLE = title + '标题1';
data.NEWS_DESC = title + '简介1';
data.NEWS_CATE_ID = cateId;
data.NEWS_CATE_NAME = title;
data.NEWS_CONTENT = [{ type: 'text', val: title + '内容1' }];
data.NEWS_PIC = [CONST_PIC];
await NewsModel.insert(data);
}
}
}
if (await dbUtil.isExistCollection(F('meet'))) {
let meetCnt = await MeetModel.count({});
if (meetCnt == 0) {
let meetArr = MEET_TYPE.split(',');
for (let j in meetArr) {
let title = meetArr[j].split('=')[1];
let typeId = meetArr[j].split('=')[0];
let data = {};
data.MEET_TITLE = title + '标题1';
data.MEET_STYLE_SET = {
desc: title + '简介1',
pic: CONST_PIC
};
data.MEET_ADMIN_ID = '1';
data.MEET_TYPE_ID = typeId;
data.MEET_TYPE_NAME = title;
data.MEET_CONTENT = [{ type: 'text', val: title + '内容1' }];
data.MEET_DAYS = [];
data.MEET_FORM_SET = [
{ type: 'text', title: '姓名', must: true },
{ type: 'mobile', title: '手机', must: true }
];
await MeetModel.insert(data);
}
}
}
if (await dbUtil.isExistCollection(F('album'))) {
let albumCnt = await AlbumModel.count({});
if (albumCnt == 0) {
let albumArr = ALBUM_CATE.split(',');
for (let j in albumArr) {
let title = albumArr[j].split('=')[1];
let cateId = albumArr[j].split('=')[0];
let data = {};
data.ALBUM_TITLE = title + '标题1';
data.ALBUM_CATE_ID = cateId;
data.ALBUM_CATE_NAME = title;
data.ALBUM_OBJ = { cover: [CONST_PIC], detail: [{ type: 'text', val: title + '内容1' }] };
await AlbumModel.insert(data);
}
}
}
if (await dbUtil.isExistCollection(F('product'))) {
let productCnt = await ProductModel.count({});
if (productCnt == 0) {
let productArr = PRODUCT_CATE.split(',');
for (let j in productArr) {
let title = productArr[j].split('=')[1];
let cateId = productArr[j].split('=')[0];
let data = {};
data.PRODUCT_TITLE = title + '标题1';
data.PRODUCT_CATE_ID = cateId;
data.PRODUCT_CATE_NAME = title;
data.PRODUCT_OBJ = { cover: [CONST_PIC], price: 1999, origPrice: 999, adv: '产品亮点', album: [CONST_PIC] };
await ProductModel.insert(data);
}
}
}
if (!await dbUtil.isExistCollection(F(INSTALL_CL))) {
await dbUtil.createCollection(F(INSTALL_CL));
}
}
}
module.exports = BaseProjectService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/fav_service.js
================================================
/**
* Notes: 收藏模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-05-24 07:48:00
*/
const BaseProjectService = require('./base_project_service.js');
const util = require('../../../framework/utils/util.js');
const FavModel = require('../model/fav_model.js');
class FavService extends BaseProjectService {
/** 是否收藏 */
async isFav(userId, oid) {
let where = {
FAV_OID: oid,
FAV_USER_ID: userId
}
let isFav = await FavModel.count(where);
return {
isFav
};
}
/**
* 更新某人收藏
* @param {*} userId
* @param {*} oid
* @param {*} cancelIfExist 已收藏的情况下是否取消
*/
async updateFav(userId, oid, title, type, path, cancelIfExist = true) {
let {
isFav
} = await this.isFav(userId, oid);
if (isFav > 0) {
if (cancelIfExist) {
// 取消
await this.delFav(userId, oid);
return {
isFav: 0
};
} else
return {
isFav: 1
};
}
// 保存
let data = {};
data.FAV_TITLE = title;
data.FAV_OID = oid;
data.FAV_TYPE = type;
data.FAV_PATH = path;
data.FAV_USER_ID = userId;
await FavModel.insert(data);
return {
isFav: 1
};
}
/** 删除收藏 */
async delFav(userId, oid) {
let where = {
FAV_OID: oid,
FAV_USER_ID: userId
}
let effect = await FavModel.del(where);
return {
effect
};
}
/** 我的收藏列表 */
async getMyFavList(userId, {
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
page,
size,
isTotal = true,
oldTotal = 0
}) {
orderBy = orderBy || {
'FAV_ADD_TIME': 'desc'
};
let fields = 'FAV_TITLE,FAV_ADD_TIME,FAV_OID,FAV_TYPE,FAV_PATH';
let where = {};
if (util.isDefined(search) && search) {
where.FAV_TITLE = {
$regex: '.*' + search,
$options: 'i'
};
}
where.FAV_USER_ID = userId;
return await FavModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
}
module.exports = FavService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/home_service.js
================================================
/**
* Notes: 全局/首页模块业务逻辑
* Date: 2021-03-15 04:00:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseProjectService = require('./base_project_service.js');
const setupUtil = require('../../../framework/utils/setup/setup_util.js');
const constants = require('../public/constants.js');
const NewsModel = require('../model/news_model.js');
class HomeService extends BaseProjectService {
async getSetup(key) {
return await setupUtil.get(key);
}
/**首页列表 */
async getHomeList() {
let list = await setupUtil.get(constants.SETUP_HOME_VOUCH_KEY);
if (!list || !Array.isArray(list)) list = [];
if (list.length == 0) {
let orderBy = {
'NEWS_ORDER': 'asc',
'NEWS_ADD_TIME': 'desc'
};
let fields = 'NEWS_PIC,NEWS_CATE_NAME,NEWS_TITLE,NEWS_DESC,NEWS_ADD_TIME';
let where = {};
where.NEWS_STATUS = 1; // 状态
let retList = await NewsModel.getAll(where, fields, orderBy, 10);
for (let k = 0; k < retList.length; k++) {
list.push({
type: 'news',
ext: retList[k].NEWS_CATE_NAME,
title: retList[k].NEWS_TITLE,
id: retList[k]._id,
desc: retList[k].NEWS_DESC,
pic: retList[k].NEWS_PIC
})
}
}
return list;
}
}
module.exports = HomeService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/meet_service.js
================================================
/**
* Notes: 预约模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-10 07:48:00
*/
const BaseProjectService = require('./base_project_service.js');
const util = require('../../../framework/utils/util.js');
const MeetModel = require('../model/meet_model.js');
const JoinModel = require('../model/join_model.js');
const DayModel = require('../model/day_model.js');
const LogUtil = require('../../../framework/utils/log_util.js');
const timeUtil = require('../../../framework/utils/time_util.js');
const dataUtil = require('../../../framework/utils/data_util.js');
const projectConfig = require('../public/project_config.js');
const MEET_LOG_LEVEL = 'debug';
class MeetService extends BaseProjectService {
constructor() {
super();
this._log = new LogUtil(projectConfig.MEET_LOG_LEVEL);
}
/**
* 抛出异常
* @param {*} msg
* @param {*} code
*/
AppError(msg) {
this._log.error(msg);
super.AppError(msg);
}
_meetLog(meet, func = '', msg = '') {
let str = '';
str = `[MEET=${meet.MEET_TITLE}][${func}] ${msg}`;
this._log.debug(str);
}
/** 统一获取Meet(某天) */
async getMeetOneDay(meetId, day, where, fields = '*') {
let meet = await MeetModel.getOne(where, fields);
if (!meet) return meet;
meet.MEET_DAYS_SET = await this.getDaysSet(meetId, day, day);
return meet;
}
/** 获取日期设置 */
async getDaysSet(meetId, startDay, endDay = null) {
let where = {
DAY_MEET_ID: meetId
}
if (startDay && endDay && endDay == startDay)
where.day = startDay;
else if (startDay && endDay)
where.day = ['between', startDay, endDay];
else if (!startDay && endDay)
where.day = ['<=', endDay];
else if (startDay && !endDay)
where.day = ['>=', startDay];
let orderBy = {
'day': 'asc'
}
let list = await DayModel.getAllBig(where, 'day,dayDesc,times', orderBy, 1000);
for (let k = 0; k < list.length; k++) {
delete list[k]._id;
}
return list;
}
// 按时段统计某时段报名情况
async statJoinCnt(meetId, timeMark) {
let whereJoin = {
JOIN_MEET_TIME_MARK: timeMark,
JOIN_MEET_ID: meetId
};
let ret = await JoinModel.groupCount(whereJoin, 'JOIN_STATUS');
let stat = { //统计数据
succCnt: ret['JOIN_STATUS_1'] || 0, //1=预约成功,
cancelCnt: ret['JOIN_STATUS_10'] || 0, //10=已取消,
adminCancelCnt: ret['JOIN_STATUS_99'] || 0, //99=后台取消
};
let whereDay = {
DAY_MEET_ID: meetId,
day: this.getDayByTimeMark(timeMark)
};
let day = await DayModel.getOne(whereDay, 'times');
if (!day) return;
let times = day.times;
for (let j in times) {
if (times[j].mark === timeMark) {
let data = {
['times.' + j + '.stat']: stat
}
await DayModel.edit(whereDay, data);
return;
}
}
}
// 预约前检测
async beforeJoin(userId, meetId, timeMark) {
await this.checkMeetRules(userId, meetId, timeMark);
}
// 预约逻辑
async join(userId, meetId, timeMark, forms) {
// 预约时段是否存在
let meetWhere = {
_id: meetId
};
let day = this.getDayByTimeMark(timeMark);
let meet = await this.getMeetOneDay(meetId, day, meetWhere);
if (!meet) {
this.AppError('预约时段选择错误1,请重新选择');
}
let daySet = this.getDaySetByTimeMark(meet, timeMark);
if (!daySet)
this.AppError('预约时段选择错误2,请重新选择');
let timeSet = this.getTimeSetByTimeMark(meet, timeMark);
if (!timeSet)
this.AppError('预约时段选择错误3,请重新选择');
// 规则校验
await this.checkMeetRules(userId, meetId, timeMark);
let data = {};
data.JOIN_USER_ID = userId;
data.JOIN_MEET_ID = meetId;
data.JOIN_MEET_TITLE = meet.MEET_TITLE;
data.JOIN_MEET_DAY = daySet.day;
data.JOIN_MEET_TIME_START = timeSet.start;
data.JOIN_MEET_TIME_END = timeSet.end;
data.JOIN_MEET_TIME_MARK = timeMark;
data.JOIN_START_TIME = timeUtil.time2Timestamp(daySet.day + ' ' + timeSet.start + ':00');
data.JOIN_FORMS = forms;
data.JOIN_STATUS = JoinModel.STATUS.SUCC;
data.JOIN_CODE = dataUtil.genRandomIntString(15);
// 入库
let joinId = await JoinModel.insert(data);
// 若有手机号码 用户入库
let mobile = '';
let userName = '';
for (let k = 0; k < forms.length; k++) {
if (!mobile && forms[k].type == 'mobile') {
mobile = forms[k].val;
continue;
} else if (!userName && forms[k].title == '姓名') {
userName = forms[k].val;
continue;
}
}
// 统计
this.statJoinCnt(meetId, timeMark);
return {
result: 'ok',
joinId
}
}
// 根据日期获取其所在天设置
getDaySetByDay(meet, day) {
for (let k = 0; k < meet.MEET_DAYS_SET.length; k++) {
if (meet.MEET_DAYS_SET[k].day == day)
return dataUtil.deepClone(meet.MEET_DAYS_SET[k]);
}
return null;
}
// 根据时段标识获取其所在天
getDayByTimeMark(timeMark) {
return timeMark.substr(1, 4) + '-' + timeMark.substr(5, 2) + '-' + timeMark.substr(7, 2);
}
// 根据时段标识获取其所在天设置
getDaySetByTimeMark(meet, timeMark) {
let day = this.getDayByTimeMark(timeMark);
for (let k = 0; k < meet.MEET_DAYS_SET.length; k++) {
if (meet.MEET_DAYS_SET[k].day == day)
return dataUtil.deepClone(meet.MEET_DAYS_SET[k]);
}
return null;
}
// 根据时段标识获取其所在时段设置
getTimeSetByTimeMark(meet, timeMark) {
let day = this.getDayByTimeMark(timeMark);
for (let k = 0; k < meet.MEET_DAYS_SET.length; k++) {
if (meet.MEET_DAYS_SET[k].day != day) continue;
for (let j in meet.MEET_DAYS_SET[k].times) {
if (meet.MEET_DAYS_SET[k].times[j].mark == timeMark)
return dataUtil.deepClone(meet.MEET_DAYS_SET[k].times[j]);
}
}
return null;
}
// 预约时段人数和状态控制校验
async checkMeetTimeControll(meet, timeMark) {
if (!meet) this.AppError('预约时段设置错误, 预约项目不存在');
let daySet = this.getDaySetByTimeMark(meet, timeMark); // 当天设置
let timeSet = this.getTimeSetByTimeMark(meet, timeMark); // 预约时段设置
if (!daySet || !timeSet) this.AppError('预约时段设置错误day&time');
let statusDesc = timeSet.status == 1 ? '开启' : '关闭';
let limitDesc = '';
if (timeSet.isLimit) {
limitDesc = '人数上限MAX=' + timeSet.limit;
} else
limitDesc = '人数不限制NO';
this._meetLog(meet, `------------------------------`);
this._meetLog(meet, `#预约时段控制,预约日期=<${daySet.day}>`, `预约时段=[${timeSet.start}-${timeSet.end}],状态=${statusDesc}, ${limitDesc} 当前预约成功人数=${timeSet.stat.succCnt}`);
if (timeSet.status == 0) this.AppError('该时段预约已经关闭,请选择其他');
// 时段总人数限制
if (timeSet.isLimit) {
if (timeSet.stat.succCnt >= timeSet.limit) {
this.AppError('该时段预约人员已满,请选择其他');
}
}
}
/** 报名规则校验 */
async checkMeetRules(userId, meetId, timeMark) {
// 预约时段是否存在
let meetWhere = {
_id: meetId
};
let day = this.getDayByTimeMark(timeMark);
let meet = await this.getMeetOneDay(meetId, day, meetWhere);
if (!meet) {
this.AppError('预约时段选择错误,请重新选择');
}
// 预约时段人数和状态控制校验
await this.checkMeetTimeControll(meet, timeMark);
// 截止规则
await this.checkMeetEndSet(meet, timeMark);
// 针对用户的次数限制
await this.checkMeetLimitSet(userId, meet, timeMark);
}
// 预约次数限制校验
async checkMeetLimitSet(userId, meet, timeMark) {
if (!meet) this.AppError('预约次数规则错误, 预约项目不存在');
let meetId = meet._id;
let daySet = this.getDaySetByTimeMark(meet, timeMark); // 当天设置
let timeSet = this.getTimeSetByTimeMark(meet, timeMark); // 预约时段设置
this._meetLog(meet, `------------------------------`);
this._meetLog(meet, `#预约次数规则,预约日期=<${daySet.day}>`, `预约时段=[${timeSet.start}~${timeSet.end}]`);
let where = {
JOIN_MEET_ID: meetId,
JOIN_MEET_TIME_MARK: timeMark,
JOIN_USER_ID: userId,
JOIN_STATUS: JoinModel.STATUS.SUCC
}
let cnt = await JoinModel.count(where);
this._meetLog(meet, `预约次数规则,mode=本时段可预约1次`, `当前已预约=${cnt}次`);
if (cnt >= 1) {
this.AppError(`您本时段已经预约,无须重复预约`);
}
}
// 预约截止设置校验
async checkMeetEndSet(meet, timeMark) {
if (!meet) this.AppError('预约截止规则错误, 预约项目不存在');
this._meetLog(meet, `------------------------------`);
let daySet = this.getDaySetByTimeMark(meet, timeMark); // 当天设置
let timeSet = this.getTimeSetByTimeMark(meet, timeMark); // 预约时段设置
this._meetLog(meet, `#预约截止规则,预约日期=<${daySet.day}>`, `预约时段=[${timeSet.start}-${timeSet.end}]`);
let nowTime = timeUtil.time('Y-M-D h:m:s');
let startTime = daySet.day + ' ' + timeSet.start + ':00';
this._meetLog(meet, `预约开始规则,mode=<时段过期判定>`, `预约开始时段=${startTime},当前时段=${nowTime}`);
if (nowTime > startTime) {
this.AppError('该时段已开始,无法预约,请选择其他');
}
}
/** 预约详情 */
async viewMeet(meetId) {
let fields = '*';
let where = {
_id: meetId,
MEET_STATUS: ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OVER]]
}
let meet = await MeetModel.getOne(where, fields);
if (!meet) return null;
let getDaysSet = [];
meet.MEET_DAYS_SET = await this.getDaysSet(meetId, timeUtil.time('Y-M-D')); //今天及以后
let daysSet = meet.MEET_DAYS_SET;
let now = timeUtil.time('Y-M-D');
for (let k = 0; k < daysSet.length; k++) {
let dayNode = daysSet[k];
if (dayNode.day < now) continue; // 排除过期
let getTimes = [];
for (let j in dayNode.times) {
let timeNode = dayNode.times[j];
// 排除状态关闭的时段
if (timeNode.status != 1) continue;
// 判断数量是否已满
if (timeNode.isLimit && timeNode.stat.succCnt >= timeNode.limit)
timeNode.error = '预约已满';
// 截止规则
if (!timeNode.error) {
try {
await this.checkMeetEndSet(meet, timeNode.mark);
} catch (ex) {
if (ex.name == 'AppError')
timeNode.error = '预约结束';
else
throw ex;
}
}
getTimes.push(timeNode);
}
dayNode.times = getTimes;
getDaysSet.push(dayNode);
}
// 只返回需要的字段
let ret = {};
ret.MEET_DAYS_SET = getDaysSet;
ret.MEET_IS_SHOW_LIMIT = meet.MEET_IS_SHOW_LIMIT;
ret.MEET_TITLE = meet.MEET_TITLE;
ret.MEET_TYPE_NAME = meet.MEET_TYPE_NAME;
ret.MEET_CONTENT = meet.MEET_CONTENT;
return ret;
}
/** 用户自助签到 */
async userSelfCheckin(userId, timeMark) {
let day = this.getDayByTimeMark(timeMark);
let today = timeUtil.time('Y-M-D');
if (day != today)
this.AppError('仅在预约当天可以签到,当前签到码的日期是' + day);
let whereSucc = {
JOIN_MEET_DAY: day,
JOIN_MEET_TIME_MARK: timeMark,
JOIN_USER_ID: userId,
JOIN_STATUS: JoinModel.STATUS.SUCC
}
let cntSucc = await JoinModel.count(whereSucc);
let whereCheckin = {
JOIN_MEET_DAY: day,
JOIN_MEET_TIME_MARK: timeMark,
JOIN_USER_ID: userId,
JOIN_IS_CHECKIN: 1,
JOIN_STATUS: JoinModel.STATUS.SUCC
}
let cntCheckin = await JoinModel.count(whereCheckin);
let ret = '';
if (cntSucc == 0) {
ret = '您没有本次报名的记录,请在「个人中心」查看详情~';
} else if (cntSucc == cntCheckin) {
ret = '您已签到,无须重复签到,请在「个人中心」查看详情~';
} else {
let where = {
JOIN_MEET_DAY: day,
JOIN_MEET_TIME_MARK: timeMark,
JOIN_USER_ID: userId,
JOIN_IS_CHECKIN: 0,
JOIN_STATUS: JoinModel.STATUS.SUCC
}
let data = {
JOIN_IS_CHECKIN: 1
}
await JoinModel.edit(where, data);
ret = '签到成功,请在「个人中心」查看详情~'
}
return {
ret
};
}
/** 预约前获取关键信息 */
async detailForJoin(userId, meetId, timeMark) {
let fields = 'MEET_DAYS_SET,MEET_FORM_SET, MEET_TITLE';
let where = {
_id: meetId,
MEET_STATUS: ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OVER]]
}
let day = this.getDayByTimeMark(timeMark);
let meet = await this.getMeetOneDay(meetId, day, where, fields);
if (!meet) return null;
let dayDesc = timeUtil.fmtDateCHN(this.getDaySetByTimeMark(meet, timeMark).day);
let timeSet = this.getTimeSetByTimeMark(meet, timeMark);
let timeDesc = timeSet.start + '~' + timeSet.end;
meet.dayDesc = dayDesc + ' ' + timeDesc;
// 取出本人最近一次本时段填写表单
let whereMy = {
JOIN_USER_ID: userId,
JOIN_MEET_ID: meetId,
JOIN_MEET_TIME_MARK: timeMark
}
let orderByMy = {
JOIN_ADD_TIME: 'desc'
}
let joinMy = await JoinModel.getOne(whereMy, 'JOIN_FORMS', orderByMy);
// 取出本人最近一次本项目填写表单
if (!joinMy) {
whereMy = {
JOIN_USER_ID: userId,
JOIN_MEET_ID: meetId,
}
let orderByMy = {
JOIN_ADD_TIME: 'desc'
}
joinMy = await JoinModel.getOne(whereMy, 'JOIN_FORMS', orderByMy);
}
// 取出本人最近一次的填写表单
if (!joinMy) {
whereMy = {
JOIN_USER_ID: userId,
}
let orderByMy = {
JOIN_ADD_TIME: 'desc'
}
joinMy = await JoinModel.getOne(whereMy, 'JOIN_FORMS', orderByMy);
}
let myForms = joinMy ? joinMy.JOIN_FORMS : [];
meet.myForms = myForms;
return meet;
}
/** 获取某天可用时段 */
async getUsefulTimesByDaysSet(meetId, day) {
let where = {
DAY_MEET_ID: meetId,
day
}
let daysSet = await DayModel.getAll(where, 'day,times');
let usefulTimes = [];
for (let k = 0; k < daysSet.length; k++) {
if (daysSet[k].day != day)
continue;
let times = daysSet[k].times;
for (let j in times) {
if (times[j].status != 1) continue;
usefulTimes.push(times[j]);
}
break;
}
return usefulTimes;
}
/** 按天获取预约项目 */
async getMeetListByDay(day) {
let where = {
MEET_STATUS: MeetModel.STATUS.COMM,
};
let orderBy = {
'MEET_ORDER': 'asc',
'MEET_ADD_TIME': 'desc'
};
let fields = 'MEET_TITLE,MEET_DAYS_SET,MEET_STYLE_SET';
let list = await MeetModel.getAll(where, fields, orderBy);
let retList = [];
for (let k = 0; k < list.length; k++) {
let usefulTimes = await this.getUsefulTimesByDaysSet(list[k]._id, day);
if (usefulTimes.length == 0) continue;
let node = {};
node.timeDesc = usefulTimes.length > 1 ? usefulTimes.length + '个时段' : usefulTimes[0].start;
node.title = list[k].MEET_TITLE;
node.pic = list[k].MEET_STYLE_SET.pic;
node._id = list[k]._id;
retList.push(node);
}
return retList;
}
/** 获取从某天开始可预约的日期 */
async getHasDaysFromDay(day) {
let where = {
day: ['>=', day],
};
let fields = 'times,day';
let list = await DayModel.getAllBig(where, fields);
let retList = [];
for (let k = 0; k < list.length; k++) {
for (let n in list[k].times) {
if (list[k].times[n].status == 1) {
retList.push(list[k].day);
break;
}
}
}
return retList;
}
/** 取得预约分页列表 */
async getMeetList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
typeId, //附加查询条件
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'MEET_ORDER': 'asc',
'MEET_ADD_TIME': 'desc'
};
let fields = 'MEET_TITLE,MEET_STYLE_SET,MEET_DAYS';
let where = {};
if (typeId && typeId !== '0') where.MEET_TYPE_ID = typeId;
console.log(typeId)
where.MEET_STATUS = ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OVER]]; // 状态
if (util.isDefined(search) && search) {
where.MEET_TITLE = {
$regex: '.*' + search,
$options: 'i'
};
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'sort':
// 排序
if (sortVal == 'view') {
orderBy = {
'MEET_VIEW_CNT': 'desc',
'MEET_ADD_TIME': 'desc'
};
}
if (sortVal == 'new') {
orderBy = {
'MEET_ADD_TIME': 'desc'
};
}
break;
}
}
let result = await MeetModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
return result;
}
/** 取消我的预约 只有成功可以取消 */
async cancelMyJoin(userId, joinId) {
let where = {
JOIN_USER_ID: userId,
_id: joinId,
JOIN_IS_CHECKIN: 0, // 签到不能取消
JOIN_STATUS: JoinModel.STATUS.SUCC
};
let join = await JoinModel.getOne(where);
if (!join) {
this.AppError('未找到可取消的预约记录');
}
// 取消规则判定
let whereMeet = {
_id: join.JOIN_MEET_ID,
MEET_STATUS: ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OVER]]
}
let meet = await this.getMeetOneDay(join.JOIN_MEET_ID, join.JOIN_MEET_DAY, whereMeet);
if (!meet) this.AppError('预约项目不存在或者已关闭');
let daySet = this.getDaySetByTimeMark(meet, join.JOIN_MEET_TIME_MARK);
let timeSet = this.getTimeSetByTimeMark(meet, join.JOIN_MEET_TIME_MARK);
if (!timeSet) this.AppError('被取消的时段不存在');
let startT = daySet.day + ' ' + timeSet.start + ':00';
let startTime = timeUtil.time2Timestamp(startT);
let now = timeUtil.time();
if (now > startTime)
this.AppError('该预约已经开始,无法取消');
let data = {
JOIN_STATUS: JoinModel.STATUS.CANCEL,
JOIN_REASON: '',
JOIN_IS_CHECKIN: 0,
}
await JoinModel.edit(where, data);
this.statJoinCnt(join.JOIN_MEET_ID, join.JOIN_MEET_TIME_MARK);
}
/** 取得我的预约详情 */
async getMyJoinDetail(userId, joinId) {
let fields = 'JOIN_IS_CHECKIN,JOIN_REASON,JOIN_MEET_ID,JOIN_MEET_TITLE,JOIN_MEET_DAY,JOIN_MEET_TIME_START,JOIN_MEET_TIME_END,JOIN_STATUS,JOIN_ADD_TIME,JOIN_CODE,JOIN_FORMS';
let where = {
_id: joinId,
JOIN_USER_ID: userId
};
return await JoinModel.getOne(where, fields);
}
/** 取得我的预约分页列表 */
async getMyJoinList(userId, {
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
// 'JOIN_MEET_DAY': 'desc',
// 'JOIN_MEET_TIME_START': 'desc',
'JOIN_ADD_TIME': 'desc'
};
let fields = 'JOIN_IS_CHECKIN,JOIN_REASON,JOIN_MEET_ID,JOIN_MEET_TITLE,JOIN_MEET_DAY,JOIN_MEET_TIME_START,JOIN_MEET_TIME_END,JOIN_STATUS,JOIN_ADD_TIME';
let where = {
JOIN_USER_ID: userId
};
//where.MEET_STATUS = ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OVER]]; // 状态
if (util.isDefined(search) && search) {
where.JOIN_MEET_TITLE = {
$regex: '.*' + search,
$options: 'i'
};
} else if (sortType) {
// 搜索菜单
switch (sortType) {
case 'timedesc': { //按时间倒序
orderBy = {
'JOIN_MEET_DAY': 'desc',
'JOIN_MEET_TIME_START': 'desc',
'JOIN_ADD_TIME': 'desc'
};
break;
}
case 'timeasc': { //按时间正序
orderBy = {
'JOIN_MEET_DAY': 'asc',
'JOIN_MEET_TIME_START': 'asc',
'JOIN_ADD_TIME': 'asc'
};
break;
}
case 'today': { //今天
where.JOIN_MEET_DAY = timeUtil.time('Y-M-D');
break;
}
case 'tomorrow': { //明日
where.JOIN_MEET_DAY = timeUtil.time('Y-M-D', 86400);
break;
}
case 'succ': { //预约成功
where.JOIN_STATUS = JoinModel.STATUS.SUCC;
//where.JOIN_MEET_DAY = ['>=', timeUtil.time('Y-M-D')];
//where.JOIN_MEET_TIME_START = ['>=', timeUtil.time('h:m')];
break;
}
case 'cancel': { //已取消
where.JOIN_STATUS = ['in', [JoinModel.STATUS.CANCEL, JoinModel.STATUS.ADMIN_CANCEL]];
break;
}
}
}
let result = await JoinModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
return result;
}
/** 取得我的某日预约列表 */
async getMyJoinSomeday(userId, day) {
let fields = 'JOIN_IS_CHECKIN,JOIN_MEET_ID,JOIN_MEET_TITLE,JOIN_MEET_DAY,JOIN_MEET_TIME_START,JOIN_MEET_TIME_END,JOIN_STATUS,JOIN_ADD_TIME';
let where = {
JOIN_USER_ID: userId,
JOIN_MEET_DAY: day
};
//where.MEET_STATUS = ['in', [MeetModel.STATUS.COMM, MeetModel.STATUS.OVER]]; // 状态
let orderBy = {
'JOIN_MEET_TIME_START': 'asc',
'JOIN_ADD_TIME': 'desc'
}
return await JoinModel.getAll(where, fields, orderBy);
}
}
module.exports = MeetService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/news_service.js
================================================
/**
* Notes: 资讯模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-10-29 07:48:00
*/
const BaseProjectService = require('./base_project_service.js');
const util = require('../../../framework/utils/util.js');
const NewsModel = require('../model/news_model.js');
class NewsService extends BaseProjectService {
/** 浏览资讯信息 */
async viewNews(id) {
let fields = '*';
let where = {
_id: id,
NEWS_STATUS: 1
}
let news = await NewsModel.getOne(where, fields);
if (!news) return null;
return news;
}
/** 取得分页列表 */
async getNewsList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
cateId, //附加查询条件
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'NEWS_ORDER': 'asc',
'NEWS_ADD_TIME': 'desc'
};
let fields = 'NEWS_PIC,NEWS_VIEW_CNT,NEWS_TITLE,NEWS_DESC,NEWS_CATE_ID,NEWS_ADD_TIME,NEWS_ORDER,NEWS_STATUS,NEWS_CATE_NAME,NEWS_OBJ';
let where = {};
where.NEWS_STATUS = 1; // 状态
if (cateId && cateId !== '0') where.NEWS_CATE_ID = cateId;
if (util.isDefined(search) && search) {
where.NEWS_TITLE = {
$regex: '.*' + search,
$options: 'i'
};
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'sort': {
orderBy = this.fmtOrderBySort(sortVal, 'NEWS_ADD_TIME');
break;
}
}
}
return await NewsModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
}
module.exports = NewsService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/passport_service.js
================================================
/**
* Notes: passport模块业务逻辑
* Date: 2020-10-14 07:48:00
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
*/
const BaseProjectService = require('./base_project_service.js');
const cloudBase = require('../../../framework/cloud/cloud_base.js');
const UserModel = require('../model/user_model.js');
const dataUtil = require('../../../framework/utils/data_util.js');
class PassportService extends BaseProjectService {
// 注册
async register(userId, {
mobile,
name,
forms,
status
}) {
// 判断是否存在
let where = {
USER_MINI_OPENID: userId
}
let cnt = await UserModel.count(where);
if (cnt > 0)
return await this.login(userId);
where = {
USER_MOBILE: mobile
}
cnt = await UserModel.count(where);
if (cnt > 0) this.AppError('该手机已注册');
// 入库
let data = {
USER_MINI_OPENID: userId,
USER_MOBILE: mobile,
USER_NAME: name,
USER_OBJ: dataUtil.dbForms2Obj(forms),
USER_FORMS: forms,
USER_STATUS: Number(status)
}
await UserModel.insert(data);
return await this.login(userId);
}
/** 获取手机号码 */
async getPhone(cloudID) {
let cloud = cloudBase.getCloud();
let res = await cloud.getOpenData({
list: [cloudID], // 假设 event.openData.list 是一个 CloudID 字符串列表
});
if (res && res.list && res.list[0] && res.list[0].data) {
let phone = res.list[0].data.phoneNumber;
return phone;
} else
return '';
}
/** 取得我的用户信息 */
async getMyDetail(userId) {
let where = {
USER_MINI_OPENID: userId
}
let fields = 'USER_MOBILE,USER_NAME,USER_FORMS,USER_OBJ,USER_STATUS,USER_CHECK_REASON'
return await UserModel.getOne(where, fields);
}
/** 修改用户资料 */
async editBase(userId, {
mobile,
name,
forms
}) {
let whereMobile = {
USER_MOBILE: mobile,
USER_MINI_OPENID: ['<>', userId]
}
let cnt = await UserModel.count(whereMobile);
if (cnt > 0) this.AppError('该手机已注册');
let where = {
USER_MINI_OPENID: userId
}
let user = await UserModel.getOne(where);
if (!user) return;
let data = {
USER_MOBILE: mobile,
USER_NAME: name,
USER_OBJ: dataUtil.dbForms2Obj(forms),
USER_FORMS: forms,
};
if (user.USER_STATUS == UserModel.STATUS.UNCHECK)
data.USER_STATUS = UserModel.STATUS.UNUSE;
await UserModel.edit(where, data);
}
/** 登录 */
async login(userId) {
let where = {
'USER_MINI_OPENID': userId
};
let fields = 'USER_ID,USER_MINI_OPENID,USER_NAME,USER_PIC,USER_STATUS';
let user = await UserModel.getOne(where, fields);
let token = {};
if (user) {
// 正常用户
token.id = user.USER_MINI_OPENID;
token.key = user.USER_ID;
token.name = user.USER_NAME;
token.pic = user.USER_PIC;
token.status = user.USER_STATUS;
// 异步更新最近更新时间
let dataUpdate = {
USER_LOGIN_TIME: this._timestamp
};
UserModel.edit(where, dataUpdate);
UserModel.inc(where, 'USER_LOGIN_CNT', 1);
} else
token = null;
return {
token
};
}
}
module.exports = PassportService;
================================================
FILE: cloudfunctions/mcloud/project/TRIP1/service/product_service.js
================================================
/**
* Notes: 产品模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-08 07:48:00
*/
const BaseProjectService = require('./base_project_service.js');
const util = require('../../../framework/utils/util.js');
const ProductModel = require('../model/product_model.js');
class ProductService extends BaseProjectService {
/** 浏览资讯信息 */
async viewProduct(id) {
let fields = '*';
let where = {
_id: id,
PRODUCT_STATUS: ProductModel.STATUS.COMM
}
let product = await ProductModel.getOne(where, fields);
if (!product) return null;
ProductModel.inc(id, 'PRODUCT_VIEW_CNT', 1);
return product;
}
/** 取得分页列表 */
async getProductList({
search, // 搜索条件
sortType, // 搜索菜单
sortVal, // 搜索菜单
orderBy, // 排序
page,
size,
isTotal = true,
oldTotal
}) {
orderBy = orderBy || {
'PRODUCT_ORDER': 'asc',
'PRODUCT_ADD_TIME': 'desc'
};
let fields = 'PRODUCT_VIEW_CNT,PRODUCT_TITLE,PRODUCT_CATE_ID,PRODUCT_ADD_TIME,PRODUCT_ORDER,PRODUCT_STATUS,PRODUCT_CATE_NAME,PRODUCT_OBJ';
let where = {};
where.and = {
_pid: this.getProjectId() //复杂的查询在此处标注PID
};
where.and.PRODUCT_STATUS = ProductModel.STATUS.COMM; // 状态
if (util.isDefined(search) && search) {
where.or = [
{ PRODUCT_TITLE: ['like', search] },
];
} else if (sortType && util.isDefined(sortVal)) {
// 搜索菜单
switch (sortType) {
case 'cateId': {
if (sortVal) where.and.PRODUCT_CATE_ID = String(sortVal);
break;
}
case 'sort': {
orderBy = this.fmtOrderBySort(sortVal, 'PRODUCT_ADD_TIME');
break;
}
}
}
return await ProductModel.getList(where, fields, orderBy, page, size, isTotal, oldTotal);
}
}
module.exports = ProductService;
================================================
FILE: miniprogram/app.js
================================================
const setting = require('./setting/setting.js');
App({
onLaunch: function (options) {
if (!wx.cloud) {
console.error('请使用 2.2.3 或以上的基础库以使用云能力')
} else {
wx.cloud.init({
// env 参数说明:
// env 参数决定接下来小程序发起的云开发调用(wx.cloud.xxx)会默认请求到哪个云环境的资源
// 此处请填入环境 ID, 环境 ID 可打开云控制台查看
// 如不填则使用默认环境(第一个创建的环境)
// env: 'my-env-id',
env: setting.CLOUD_ID,
traceUser: true,
})
}
this.globalData = {};
// 用于自定义导航栏
wx.getSystemInfo({
success: e => {
this.globalData.statusBar = e.statusBarHeight;
let capsule = wx.getMenuButtonBoundingClientRect();
if (capsule) {
this.globalData.custom = capsule;
this.globalData.customBar = capsule.bottom + capsule.top - e.statusBarHeight;
} else {
this.globalData.customBar = e.statusBarHeight + 50;
}
}
});
},
})
================================================
FILE: miniprogram/app.json
================================================
{
"pages": [
"projects/TRIP1/pages/default/index/default_index",
"projects/TRIP1/pages/about/index/about_index",
"projects/TRIP1/pages/search/search",
"projects/TRIP1/pages/my/index/my_index",
"projects/TRIP1/pages/my/reg/my_reg",
"projects/TRIP1/pages/my/edit/my_edit",
"projects/TRIP1/pages/my/foot/my_foot",
"projects/TRIP1/pages/my/fav/my_fav",
"projects/TRIP1/pages/news/index/news_index",
"projects/TRIP1/pages/news/detail/news_detail",
"projects/TRIP1/pages/news/cate1/news_cate1",
"projects/TRIP1/pages/news/cate2/news_cate2",
"projects/TRIP1/pages/admin/news/list/admin_news_list",
"projects/TRIP1/pages/admin/news/add/admin_news_add",
"projects/TRIP1/pages/admin/news/edit/admin_news_edit",
"projects/TRIP1/pages/admin/setup/about/admin_setup_about",
"projects/TRIP1/pages/admin/setup/about_list/admin_setup_about_list",
"projects/TRIP1/pages/admin/setup/qr/admin_setup_qr",
"projects/TRIP1/pages/admin/index/home/admin_home",
"projects/TRIP1/pages/admin/index/login/admin_login",
"projects/TRIP1/pages/admin/content/admin_content",
"projects/TRIP1/pages/admin/mgr/log/admin_log_list",
"projects/TRIP1/pages/admin/mgr/edit/admin_mgr_edit",
"projects/TRIP1/pages/admin/mgr/list/admin_mgr_list",
"projects/TRIP1/pages/admin/mgr/add/admin_mgr_add",
"projects/TRIP1/pages/admin/mgr/pwd/admin_mgr_pwd",
"projects/TRIP1/pages/admin/user/list/admin_user_list",
"projects/TRIP1/pages/admin/user/detail/admin_user_detail",
"projects/TRIP1/pages/admin/user/export/admin_user_export",
"projects/TRIP1/pages/meet/index/meet_index",
"projects/TRIP1/pages/meet/calendar/meet_calendar",
"projects/TRIP1/pages/meet/join/meet_join",
"projects/TRIP1/pages/meet/detail/meet_detail",
"projects/TRIP1/pages/meet/self/meet_self",
"projects/TRIP1/pages/meet/my_join_list/meet_my_join_list",
"projects/TRIP1/pages/meet/my_join_detail/meet_my_join_detail",
"projects/TRIP1/pages/admin/meet/cover/admin_meet_cover",
"projects/TRIP1/pages/admin/meet/edit/admin_meet_edit",
"projects/TRIP1/pages/admin/meet/export/admin_join_export",
"projects/TRIP1/pages/admin/meet/join/admin_meet_join",
"projects/TRIP1/pages/admin/meet/list/admin_meet_list",
"projects/TRIP1/pages/admin/meet/record/admin_record_list",
"projects/TRIP1/pages/admin/meet/scan/admin_meet_scan",
"projects/TRIP1/pages/admin/meet/self/admin_meet_self",
"projects/TRIP1/pages/admin/meet/temp/admin_temp_select",
"projects/TRIP1/pages/admin/meet/time/admin_meet_time",
"projects/TRIP1/pages/album/detail/album_detail",
"projects/TRIP1/pages/album/index/album_index",
"projects/TRIP1/pages/admin/album/list/admin_album_list",
"projects/TRIP1/pages/admin/album/add/admin_album_add",
"projects/TRIP1/pages/admin/album/edit/admin_album_edit",
"projects/TRIP1/pages/product/detail/product_detail",
"projects/TRIP1/pages/product/index/product_index",
"projects/TRIP1/pages/admin/product/list/admin_product_list",
"projects/TRIP1/pages/admin/product/add/admin_product_add",
"projects/TRIP1/pages/admin/product/edit/admin_product_edit",
"projects/TRIP1/pages/about/service/about_service",
"cmpts/public/form/form_set/field/form_set_field",
"cmpts/public/form/form_show/content/form_show_content",
"pages/test1/test1"
],
"window": {
"backgroundColor": "#f1f1f1",
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#00C176",
"navigationBarTitleText": "旅游景区门户小程序",
"navigationBarTextStyle": "white"
},
"tabBar": {
"custom": false,
"backgroundColor": "#FFFFFF",
"color": "#999999",
"selectedColor": "#00C176",
"list": [
{
"pagePath": "projects/TRIP1/pages/default/index/default_index",
"text": "首页",
"iconPath": "/projects/TRIP1/images/tabbar/home.png",
"selectedIconPath": "/projects/TRIP1/images/tabbar/home_cur.png"
},
{
"pagePath": "projects/TRIP1/pages/product/index/product_index",
"text": "景点",
"iconPath": "/projects/TRIP1/images/tabbar/jing.png",
"selectedIconPath": "/projects/TRIP1/images/tabbar/jing_cur.png"
},
{
"pagePath": "projects/TRIP1/pages/album/index/album_index",
"text": "攻略",
"iconPath": "/projects/TRIP1/images/tabbar/gong.png",
"selectedIconPath": "/projects/TRIP1/images/tabbar/gong_cur.png"
},
{
"pagePath": "projects/TRIP1/pages/about/service/about_service",
"text": "服务",
"iconPath": "/projects/TRIP1/images/tabbar/service.png",
"selectedIconPath": "/projects/TRIP1/images/tabbar/service_cur.png"
},
{
"pagePath": "projects/TRIP1/pages/my/index/my_index",
"text": "我的",
"iconPath": "/projects/TRIP1/images/tabbar/my.png",
"selectedIconPath": "/projects/TRIP1/images/tabbar/my_cur.png"
}
]
},
"permission": {
"scope.userLocation": {
"desc": "获取你当前位置信息用于小程序位置接口的效果展示"
}
},
"usingComponents": {
"cmpt-comm-list": "/cmpts/public/list/comm_list_cmpt",
"cmpt-picker": "/cmpts/public/picker/picker_cmpt",
"cmpt-modal": "/cmpts/public/modal/modal_cmpt"
},
"sitemapLocation": "sitemap.json"
}
================================================
FILE: miniprogram/app.wxss
================================================
@import "style/base/comm.wxss";
@import "style/public/project.wxss";
================================================
FILE: miniprogram/cmpts/biz/detail/detail_cmpt.js
================================================
const pageHelper = require('../../../helper/page_helper');
const posterCmptHelper = require('../../public/poster/poster_cmpt_helper.js');
const FavBiz = require('../../../comm/biz/fav_biz.js');
const FootBiz = require('../../../comm/biz/foot_biz.js');
Component({
options: {
addGlobalClass: true
},
/**
* 组件的属性列表
*/
properties: {
mode: {
type: String,
value: 'mode1'
},
oid: {
type: String,
value: ''
},
cate: {
type: String,
value: ''
},
title: {
type: String,
value: ''
},
cover: {
type: String,
value: ''
},
desc: {
type: String,
value: '查看详情'
},
qr: {
type: String,
value: ''
},
bg: {
type: String,
value: ''
},
tag: {
type: String, //小角标
value: ''
},
doFav: {
type: Boolean,
value: true
},
doFoot: {
type: Boolean,
value: true
},
doShare: {
type: Boolean,
value: true
},
doHome: {
type: Boolean,
value: true
},
doPoster: {
type: Boolean,
value: true
},
doFoot: {
type: Boolean,
value: true
},
doTop: {
type: Boolean,
value: true
},
doSlot: {
type: Boolean,
value: false
},
topBtnBottom: {
type: Number,
value: 190
},
topBtnShow: {
type: Boolean,
value: false
},
},
/**
* 组件的初始数据
*/
data: {
isFav: -1,
showPoster: false,
posterConfig: null,
},
lifetimes: {
created: function () {
},
attached: function () {
},
ready: async function () {
if (!this.data.oid || !this.data.title) return;
if (this.data.doFav) {
FavBiz.isFav(this, this.data.oid);
}
if (this.data.doFoot) {
FootBiz.addFoot(this.data.cate, this.data.title);
}
if (this.data.doShare) {
let posterConfig = await posterCmptHelper.config1({
cover: this.data.cover,
title: this.data.title,
desc: this.data.desc,
qr: this.data.qr,
bg: this.data.bg
})
this.setData({
posterConfig
});
}
},
move: function () {
},
detached: function () {
},
},
/**
* 组件的方法列表
*/
methods: {
bindShareTap: function () {
this.setData({
showPoster: true
});
},
bindFavTap: async function () {
if (this.data.isFav == -1) return;
await FavBiz.updateFav(this, this.data.oid, this.data.isFav, this.data.cate, this.data.title);
},
url: function (e) {
pageHelper.url(e, this);
},
bindHomeTap: function (e) {
let url = pageHelper.fmtURLByPID('/pages/default/index/default_index');
wx.reLaunch({ url });
},
top: function (e) {
// 回页首事件
pageHelper.top();
}
}
})
================================================
FILE: miniprogram/cmpts/biz/detail/detail_cmpt.json
================================================
{
"component": true,
"usingComponents": {
"cmpt-poster": "/cmpts/public/poster/poster_cmpt"
}
}
================================================
FILE: miniprogram/cmpts/biz/detail/detail_cmpt.wxml
================================================
首页
{{tag}}
已收藏
加入收藏
分享
{{tag}}
已收藏
收藏
================================================
FILE: miniprogram/cmpts/biz/detail/detail_cmpt.wxss
================================================
.cmpt-fixed-home-btn {
position: fixed;
bottom: 180rpx;
left: 10rpx;
font-size: 45rpx;
height: 80rpx;
width: 80rpx;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 50%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #fff;
z-index: 99999;
}
/*MODE1*/
.cmpt-biz-detail-mode1 {
box-shadow: 0 -6rpx 8rpx rgba(114, 130, 138, 0.2);
width: 100%;
position: fixed;
bottom: 0;
background-color: #f2f2f2;
z-index: 9999999;
display: flex;
align-items: stretch;
justify-content: center;
font-size: 32rpx;
color: #333;
}
.cmpt-biz-detail-mode1 .has-tag {
position: absolute;
top: 0rpx;
right: 0rpx;
z-index: 9999;
font-size: 22rpx;
padding: 0rpx 4rpx;
border-radius: 5rpx;
color: #fff;
background-color: rgba(0, 0, 0, 0.2);
}
.cmpt-biz-detail-mode1 .btn-inner {
padding: 30rpx 0rpx;
flex: 1 1 0;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.cmpt-biz-detail-mode1 .btn-inner {
border-right: 1rpx solid #ddd;
}
.cmpt-biz-detail-mode1 .btn-inner:last-child {
border-right: unset;
}
.cmpt-biz-detail-mode1 .btn-inner .fav {
background: inherit;
}
.cmpt-biz-detail-mode1 .btn-inner .share {
background: inherit;
}
/*MODE2*/
.cmpt-biz-detail-mode2 {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
position: fixed;
bottom: 0;
background-color: #fff;
box-shadow: 0 -6rpx 8rpx rgba(114, 130, 138, 0.1);
z-index: 999;
}
.cmpt-biz-detail-mode2 .has-tag {
position: absolute;
top: 5rpx;
right: 10rpx;
z-index: 9999;
font-size: 24rpx;
background-color: #f8f8f8;
padding: 5rpx 10rpx;
border-radius: 10rpx;
color: #666;
border: 1rpx solid #eee;
}
.cmpt-biz-detail-mode2 .inner {
width: 100%;
padding: 20rpx 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.cmpt-biz-detail-mode2 .inner .slot-inner {
flex: 1;
padding: 0 20rpx;
}
.cmpt-biz-detail-mode2 .share,
.cmpt-biz-detail-mode2 .fav {
font-size: 35rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0 20rpx;
width: 120rpx;
}
.cmpt-biz-detail-mode2 .share .text-s,
.cmpt-biz-detail-mode2 .fav .text-s {
margin-top: 0rpx;
font-size: 24rpx;
}
/*MODE RIGHT*/
.cmpt-biz-detail-mode-right {
display: flex;
align-items: center;
justify-content: flex-start;
flex-direction: column;
position: fixed;
bottom: 90rpx;
right:15rpx;
z-index: 99999;
}
.cmpt-biz-detail-mode-right .fixed-btn {
font-size: 35rpx;
height: 60rpx;
width: 60rpx;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
margin-bottom: 15rpx;
}
.cmpt-biz-detail-mode-right .fixed-btn .text-t{
font-size: 22rpx;
}
================================================
FILE: miniprogram/cmpts/biz/foot/foot_cmpt.js
================================================
const pageHelper = require('../../../helper/page_helper');
const setting = require('../../../setting/setting.js');
Component({
options: {
addGlobalClass: true
},
/**
* 组件的属性列表
*/
properties: {
color: {
type: String,
value: ''
},
},
/**
* 组件的初始数据
*/
data: {
},
lifetimes: {
created: function () {
// 组件实例化,但节点树还未导入,因此这时不能用setData
},
attached: function () {
// 在组件实例进入页面节点树时执行
// 节点树完成,可以用setData渲染节点,但无法操作节点
},
ready: async function () {
// 组件布局完成,这时可以获取节点信息,也可以操作节点
// 当前用户,用于评论删除
this._loadDetail();
},
move: function () {
// 组件实例被移动到树的另一个位置
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
_loadDetail: async function () {
this.setData({
company: setting.COMPANY,
ver: setting.VER
});
},
url: function (e) {
pageHelper.url(e, this);
}
}
})
================================================
FILE: miniprogram/cmpts/biz/foot/foot_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/biz/foot/foot_cmpt.wxml
================================================
================================================
FILE: miniprogram/cmpts/biz/foot/foot_cmpt.wxss
================================================
@import "../../../style/public/project.wxss";
.site-footer {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 24rpx;
color: #aaa;
margin-top: 20rpx;
margin-bottom: 20rpx;
}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_comm/calendar_comm_cmpt.js
================================================
const timeHelper = require('../../../../helper/time_helper.js');
const pageHelper = require('../../../../helper/page_helper.js');
const calendarLib = require('../calendar_lib.js');
/*#### 父组件日历颜色定义*/
/* 整体颜色 */
//--calendarPageColor: #F0F4FF;
/* 加重颜色*/
//--calendarMainColor: #388AFF;
/* 加重的亮颜色 用于选中日期的数据小圆点 */
//--calendarLightColor: #A2C7FF;
Component({
options: {
addGlobalClass: true
},
properties: {
isLunar: { //是否开启农历
type: Boolean,
value: true
},
mode: { // 模式 one/multi
type: String,
value: 'one'
},
year: { // 正在操作的年
type: Number,
value: 0
},
month: { // 正在操作的月
type: Number,
value: 0
},
fold: { //日历折叠
type: Boolean,
value: false
},
selectTimeout: { //过期时间选择(mode=multi)
type: Boolean,
value: true
},
selectTimeoutHint: { //过期时间选择提示(mode=multi)
type: String,
value: '不能选择过去的日期'
},
hasDays: { // 有数据的日期
type: Array,
value: [],
observer: function (newVal, oldVal) {
if (newVal.length != oldVal.length) {
// TODO 引起加载的时候二次调用
//this._init();
}
}
},
oneDoDay: { // 正在操作的天 string
type: String,
value: null
},
multiDoDay: { // 多选模式>正在操作的天 arrary[]
type: Array,
value: [],
},
multiOnlyOne: { //多选模式>只能选一个
type: Boolean,
value: false
}
},
data: {
weekNo: 0, // 正在操作的那天位于第几周
fullToday: 0, //今天
},
lifetimes: {
attached() {
this._init();
}
},
methods: {
_init: function () {
calendarLib.getNowTime(this);
calendarLib.createDay(this);
},
bindFoldTap: function (e) { // 日历折叠
calendarLib.bindFoldTap(this);
},
bindNextTap(e) { // 下月
calendarLib.bindNextTap(this);
},
bindLastTap(e) { // 上月
calendarLib.bindLastTap(this);
},
bindDayOneTap(e) { // 单个天点击
let day = e.currentTarget.dataset.fullday;
let now = timeHelper.time('Y-M-D');
if (day < now)
return pageHelper.showNoneToast('已过期', 1000);
calendarLib.bindDayOneTap(e, this);
},
bindDayMultiTap(e) { // 多选天点击
// 过期时间判断
if (!this.data.selectTimeout) {
let day = e.currentTarget.dataset.fullday;
let now = timeHelper.time('Y-M-D');
if (day < now)
return pageHelper.showNoneToast(this.data.selectTimeoutHint);
}
calendarLib.bindDayMultiTap(e, this);
},
bindToNowTap: function (e) { // 回本月
calendarLib.bindToNowTap(this);
},
// ListTouch触摸开始
listTouchStart(e) {
pageHelper.listTouchStart(e, this);
},
// ListTouch计算方向
listTouchMove(e) {
pageHelper.listTouchMove(e, this);
},
/** ListTouch计算滚动 */
listTouchEnd: function (e) {
calendarLib.listTouchEnd(this);
}
}
})
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_comm/calendar_comm_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_comm/calendar_comm_cmpt.wxml
================================================
// 比较操作日期所在月是否当前显示的月
function compareYearMonth(oneDoDay, year, month) {
var arr = oneDoDay.split('-');
return arr[0] == year && arr[1] == month;
}
module.exports = {
compareYearMonth: compareYearMonth,
};
本月
{{year}}年{{month}}月
一
二
三
四
五
六
日
{{item.show}}
{{item.show}}
{{item.lunar}}
{{item.show}}
{{item.show}}
{{item.lunar}}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_comm/calendar_comm_cmpt.wxss
================================================
@import "./din.wxss";
page {
/*#### 父组件日历颜色定义*/
/* 整体颜色 */
--calendarPageColor: #fff;
/* 加重颜色*/
--calendarMainColor: #1F6ED4;
/* 加重的亮颜色 用于选中日期的数据小圆点 */
--calendarLightColor: #A2C7FF;
}
.calendar-text {
color: var(--calendarMainColor) !important
}
.calendar-bg {
background-color: var(--calendarMainColor) !important
}
.cal-container {
width: 100%;
padding-bottom: 10rpx;
background-color: var(--calendarPageColor)
}
.cal-container .cal-nav {
position: relative;
width: 100%;
min-height: 80rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 0 20rpx;
color: #666;
font-weight: bold;
background-color: #f8f8f8;
}
.cal-container .cal-nav .select-item {
width: 500rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.cal-container .cal-nav .arrow {
width: 150rpx;
font-size: 40rpx;
}
.cal-container .cal-nav .fold {
position: absolute;
right: 0rpx;
width: 100rpx;
font-size: 40rpx;
font-weight: bold;
}
.cal-container .cal-nav .to-now {
position: absolute;
left: 5rpx;
width: 100rpx;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
}
.cal-main {
width: 100%;
padding: 0rpx 0rpx;
}
.cal-title {
display: flex;
width: 100%;
border-bottom: 2rpx dashed #ccc;
background-color: #f8f8f8;
}
.cal-title .cal-title-view {
width: 14.28%;
height: 70rpx;
display: flex;
justify-content: center;
align-items: center;
color: #333;
font-size: 32rpx;
font-weight: bold;
}
.cal-center {
display: flex;
flex-direction: row;
flex-wrap: wrap;
overflow: hidden;
justify-content: center;
align-items: center;
}
.cal-center .cube {
width: 14.28%;
display: flex;
justify-content: center;
align-items: center;
}
.cal-center .cube.lunar {
margin-bottom: 0rpx;
}
.cal-center .num-grid {
min-width: 70rpx;
height: 70rpx;
display: flex;
justify-content: center;
align-items: center;
color: #333;
margin-bottom: 2rpx;
border-radius: 10rpx;
}
.cal-center .cube.lunar .num-grid {
width: 70rpx;
height: 90rpx;
}
.cal-center .num-grid.now-day-cur {
background-color: #fadbd9;
color: orangered;
font-weight: bold;
}
.cal-center .num-grid.now-day-cur.calendar-bg {
color: #fbbd08 !important;
}
.cal-center .num {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 32rpx;
font-family: 'din';
}
.cal-center .num .lunar {
font-size: 20rpx;
font-weight: normal;
color: #aaa;
}
.cal-center .text-no-month {
color: #333;
opacity: .5;
}
.cal-center .calendar-bg .text-no-month {
color: #ccc;
opacity: 1;
}
/* 当日有数据 */
.data-has {
position: relative;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.data-has::before {
position: absolute;
content: '';
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background-color: var(--calendarMainColor);
right: -16rpx;
top: 14rpx;
}
.calendar-bg .data-has::before {
background-color: var(--calendarLightColor);
}
/* 选中某日 */
.cube .data-checked {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.cube .data-checked::before {
position: absolute;
content: '';
width: 17rpx;
height: 17rpx;
transform: rotate(45deg);
border-right: 3rpx solid #fff;
border-bottom: 3rpx solid #fff;
bottom: 13rpx;
}
.cube.lunar .data-checked::before {
bottom: 38rpx;
}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_comm/din.wxss
================================================
@font-face {
font-family: 'din';
src: url('data:font/ttf;charset=utf-8;base64,AAEAAAAPAIAAAwBwRkZUTYgLMMEAAlaAAAAAHEdERUY15TphAAHagAAAAI5HUE9TXpiR2QAB9UgAAGE4R1NVQgGZl0kAAdsQAAAaNk9TLzJhprB7AAABeAAAAGBjbWFwlg4SlgAAEUAAAAVOZ2FzcP//AAMAAdp4AAAACGdseWZKMGmsAAAeVAABkBhoZWFkGyNVqwAAAPwAAAA2aGhlYRFbC3oAAAE0AAAAJGhtdHiGGBg+AAAB2AAAD2hsb2NhAkJmIgAAFpAAAAfEbWF4cAQqAFsAAAFYAAAAIG5hbWUcsXOaAAGubAAAB3Fwb3N0m0IygAABteAAACSWAAEAAAAEAAB/+YqcXw889QALCAAAAAAA1FMptQAAAADeE+AD/Zz9rgv+CUgAAAAIAAIAAAAAAAAAAQAAB779/gAADDv9nP1tC/4AAQAAAAAAAAAAAAAAAAAAA9MAAQAAA+EAWAAHAAAAAAACAAAAAQABAAAAQAAAAAAAAAAEBN0B9AAFAAAFMwTNAAAAmgUzBM0AAALNAGYCkQAAAAAGAAAAAAAAACAAAAcAAAABAAAAAAAAAABVTEEgAMAADfsCB779/gAACaQCuCAAAZMAAAAAAhcCvAAAACAABgSyAFIAAAAAAqoAAAAAAAACZgAAAmYAAAHGAHEDEAB7BdIAMQUGAC8H5wBMBVoAcwHAAHsCmwCWAsoAzwLZADMEAgBIAcQARgL1AHkBzABaArL/zQTvAFYE7wDTBO8ASgTvACUE7wBEBO8APwTvAF4E7wBcBO8ATgTvAEwBzABaAcgARgQCAEgEAgBIBAIARgObAAgHqQA/Bfn//gWsALAFkQA/Bi8AsAVJALAEqQCwBfkAPwZRALACcgCwBBL/+gXOALAEUwCwB14AsAZRALAGeAA/BWYAsAaHAD8FyACwBQYALwS0ABAGGACTBZsABAhiAAQFoQAbBTH//gVYAEgCzACwBCgAyQOyARcEYAA/BJEAAALMAGoEuABMBXIAsASDAEIFdgBCBLwAQgLXAEgFIgBCBWAAsAJTAJwCXv9QBNcAsAJqALAIUwCmBVMApgUGAEIFcgCwBXYAQgM5AKYD2wApA1EANwU/AIMES//+Bz3//gSLABcEWv/yBFMAVgKwAEICNwC2AzEA5QQCAIMCZgAAAcYAcwTMAEIEaABIBakAQgUzAAACNwC2BBYAKQMOAEgF6wA/AxgAOwPAACUEhwBIAvUAeQXrAD8CzABaAtQApgQCAEgDFAAzAt0AFALMAHUFagCwBQD//AHCAFoCzACYAh4AHwNFADcD1ACDBqwAHwbbAB8HKwAUA5sAPQX5//4F+f/+Bfn//gX5//4F+f/+Bfn//giF//4FkQA/BUkAsAVJALAFSQCwBUkAsAJy/+UCcgCmAnL/7AJy//wGXgAABlEAsAZ4AD8GeAA/BngAPwZ4AD8GeAA/BAIAiwZ4AD8GGACTBhgAkwYYAJMGGACTBTH//gVyALAFPwCWBLgATAS4AEwEuABMBLgATAS4AEwEuABMB8gATASDAEIEvABCBLwAQgS8AEIEvABCAlP/1wJTAJgCU//dAlP/7gTAAEQFUwCmBQYAQgUGAEIFBgBCBQYAQgUGAEIEAgBIBQYAQgU/AIMFPwCDBT8AgwU/AIMEWv/yBXIAsARa//IF+f/+BLgATAX5//4EuABMBfn//gTAAEwFkQA/BIMAQgWRAD8EgwBCBZEAPwSDAEIFkQA/BIMAQgYvALAGJABCBl4AAAV4AEIFSQCwBLwAQgVJALAEvABCBUkAsAS8AEIFWgCwBLwAQgVJALAEvABCBfkAPwUiAEIF+QA/BSIAQgX5AD8FIgBCBfkAPwUiAEIGUQCwBWD/4wbXAD0FYAAGAnL/5wJT/9kCcgArAlMAHwJyAAwCU//+AnIAnAIvAHkCcgCqAlMApgaFALAEsgCcBBL/+gJD/0YFzgCwBNcAsATKAKYEUwCoAmoApARTALACagCcBFMAsAMYALAEUwCwAv0AsARa//QCsP/2BlEAsAVTAKYGUQCwBVMApgZRALAFUwCmBjsALwZRALAFUwCmBngAPwUGAEIGeAA/BQYAQgZ4AD8FBgBCCM4APwhHAEIFyACwAzkApgXIALADOQCLBcgAsAM5AIEFBgAvA9sAKQUGAC8D2wApBQYALwPbACkFBgAvA9sAKQS0ABADUQA3BLQAEANaADcEtAAQA1EANwYYAJMFPwCDBhgAkwU/AIMGGACTBT8AgwYYAJMFPwCDBhgAkwU/AIMGGgCWBTMAgwhiAAQHPf/+BTH//gRa//IFMf/+BVgASARTAFYFWABIBFMAVgVYAEgEUwBWBkMAOwMO/xAGeAA/BQYAQgYYAJMFPwCDC4cAsAqDALAJygBCCGYAsAayALAEyACwCmQAsAiwALAHsgCmBfkAPwUiAEIGeAA/BQYAQgX5//4EuABMCIX//gfIAEwGeAA/BQYAQgX5//4EuABMBfn//gS4AEwFSQCwBLwAQgVJALAEvABCAnL/fwJT/3ECcgAMAlP//gZ4AD8FBgBCBngAPwUGAEIFyACwAzkAMQXIALADOQCmBhgAkwU/AIMGGACTBT8AgwUGAC8D2wApBLQAEANRADcGeAA/BQYAQgZ4AD8FBgBCBngAPwUGAEIFMf/+BFr/8gJD/0YEvAAtAAD/FAF8AC8CzAFmAswAcwLMABkCzAACAQ4AKQLMAFoCzAB1AswAagEOACkCzAA5AswA1wLMAHUCzACwAswAFALM//IAAP7LAAD/fQAA/rIAAP6uAAD+8gAA/tMAAP9xAAD+wQAA/xkAAP8OAAD+iwAA/pwAAP5GAAD+0wAA/30AAAAAAAD/cQAA/sEAAP9kAAD/BAAA/0YAAP7TAAD+8gAA/ZwF+f/+BwAAEAVqALAFzv/lBZEAPwSDAEIGLwCwBXYAQgYvALAFdgBCBUkAsAS8AEIFSQCwBLwAQgVJALAEvABCBfkAPwUiAEIGUQCwBWAAsAZRALAFYACwAnL/+gJT/+wEUwCwAmoAqARTALACagApB14AsAhTAKYGUQCwBVMApgZRALAFUwCmBlEAsAVTAKYGeAA/BQYAQgZ4AD8FBgBCBngAPwUGAEIGeAA/BQYAQgXIALADOQCYBcgAsAM5ABkFBgAvA9sAKQUGAC8D2wApBQYALwPbACkFBgAvA9sAKQUGAC8D2wApBLQAEANRADcEtAAQA1EANwYYAJMFPwCDBhgAkwU/AIMIYgAEBz3//ghiAAQHPf/+CGIABAc9//4FMf/+BFr/8gVYAEgEUwBWA1EAGQV8AJYF+f/+BLgATAX5//4EuABMBfn//gS4AEwF+f/+BLgATAX5//4EuABMBfn//gS4AEwF+f/+BLgATAX5//4EuABMBfn//gS4AEwF+f/+BLgATAX5//4EuABMBfn//gS4AEwFSQCwBLwAQgVJALAEvABCBUkAsAS8AEIFSQCwBLwAQgVJALAEvABCBUkAsAS8AEIFSQCwBLwAQgVJALAEvABCAnIAVAJTAEYCcgCqAlMAnAZ4AD8FBgBCBngAPwUGAEIGeAA/BQYAQgZ4AD8FBgBCBngAPwUGAEIGeAA/BQYAQgZ4AD8FBgBCBngAPwUGAEIGeAA/BQYAQgZ4AD8FBgBCBngAPwUGAEIGeAA/BQYAQgYYAJMFPwCDBhgAkwU/AIMGGACTBT8AgwYYAJMFPwCDBhgAkwU/AIMGGACTBT8AgwYYAJMFPwCDBTH//gRa//IFMf/+BFr/8gUx//4EWv/yBTH//gRa//IE7wAAAcwAAAGZAAAAzAAAAAAAAAL1AHkE7wAAAz8AAAVuAAAFbgAAAcQARgHEAEYBxABGAwoARgMKAEYDCgBGBF4AIQReACECWABaBQYAWgt+AEwBtgBSAxIAUgI3ACUCTQCDALj9ywOXAEwDMQA1AwoAKwM7AEwDBgA9A0UARAM7ADsDlwBMAh4AHwMUADMC3QAUAzEANQMKACsDOwBMAwYAPQNFAEQDOwA7BZEAPwS0AB8EaABIBiAAKQw7ALAH+wACBXgAQgZPAEgFbgAhBgIAKQXMAD8FkQA/BLoAPwWNAD0GAAA7BXIAIwPAACsJpwCwByD/9AcAABAGeACPBswAHwdLABQHbgArBy0APQTtAEYD8wAABYUAAAX7ALAE5QA/BAIASAKy/80CWABaBY0AEAbzAEIC1//pBAIAgwQCAEgEAgBIBAIARgQ5AEIFKwBIBVgASAYeAJMGHgCTBh4AkwYeAJMGHgCTBh4AkwYeAJMGHgCTBh4AkwYeAJMGHgCTBh4AkwYeAJMGHgCTBh4AkwYeAJMGHgCTBh4AkwYeAJMGHgCTBh4AkwYtAJMGHgCTBh4AkwYeAJMLhwCwCoMAsASyAE4EsgBOBLIATgSyAE4EsgBOBLIATgSyAE4EsgBOBLIATgSyAE4EsgBOBLIATgSyAE4EsgBOBLIATgSyAE4EsgBOBLIATgSyAE4EsgBOBLIATgS4AFQEsgBOBE8AkwX7AD8F+wA/BfsAPwX7AD8F+wA/BfsAPwX7AD8D6wBkB/0AZAPrAGQFPwACB/0AZAPrAGQD6wBkA+sAOwPrAGQD6wBkA+sAZAPrAGQD6wBkA+sAZAPrAGQD6wBkA+sAZAPrAGQEEv/6BBL/+ghmALAJhQCwCYUAsAYkALAKNwCwBiQAsAYkALAGJACwBiQAsAYkALAGJgCwCIMAsAYkALAGJACwBngAPwYeADMFSQAzBUkAMwVJADMFSQAzBUkAMwVJADMFSQAzBiQAkQYkAJEGJACRBiQAkQYkAJEGJACRBiQAkQYkAJEGJACRBiQAkQYkAJEGJACRBiQAkQYkAJEGJACRBiQAkQYkAJEGJACRBiQAkQYkAJEGJACRBiQAkQYkAJEJZgCTCWYAkwlmAJMJZgCTCWYAkwXrAJMF6wCTBesAkwXrAJMF6wCTBesAkwXrAJMF6wCTBesAkwXrAJMFWABIBVgASAVYAEgFWABIBVgASAVoAEIFaABCBWgAQgVoAEIFaABCBWgAQgVoAEIFaABCBWgAQgVoAEIFaABCBWgAQgVoAEIFaABCBWgAQgVoAEIFaABCBWgAQgVoAEIFaABCBWgAQgVoAEIFaABCBWgAQgVoAEIHuABMB7gATAnKAEIErABCBKwAQgSsAEIErABCBKwAQgSsAEIErABCBKwAQgSsAEIErABCBKwAQgSsAEIErABCBKwAQgSsAEIErABCBKwAQgSsAEIErABCBKwAQgSsAEIEzgBCBKwAQgSsADMCkQCTBKcAlgJTAJwCiwCTAosAhQMOAJMCiwCTAt8AkwKLAJME6QCTAosAOwLG//YIIgBCAwoAiwMKAIsDEgCLAwoAiwMKAIsDCv/RAwoAiwMKAIsHyACBB8gAgQfIAIEHyACBB8gAgQU9AIMFPQCDBT0AgwU9AIMFPQCDBT0AgwU9AIMFPQCDBT0AgwRTAFYEUwBWBFMAVgRTAFYEUwBWCAwASAeLAJMKYABICdQAkwg5AEgHsACTB4kASAdDAJME+QCTBXYASAWNAGADJgAXBLoAPQRaAAQE3QA7BKcALQT1AF4EnQBIBQoAVAUAAFAFjQBgAyYAFwRiAD0EWgAEBN0AOwSnAC8E9QBeBJ0ASgUKAFQE9QBQBO8ASATvAOkE7wBgBO8AJQTvAEQE7wA/BO8AXgTvAFwE7wBOBO8ATAOXAEwCHgAfAxQAMwLdABQDMQA1AwoAKwM7AEwDBgA9A0UARAM7ADsDlwBMAh4AHwMUADMC3QAUAzEANQMKACsDOwBMAwYAPQNFAEQDOwA7ATcAFAAAAAD/ff8n/sH+4/9xAAD+4/6W/uP+uv7B/sH+wf62AAAAAwAAAAMAAAAcAAEAAAAAA0QAAwABAAAAHAAEAygAAADGAIAABgBGAAAADQB+AX4BjwGSAaEBsAHMAecB6wIbAi0CMwI3AlkCvAK/AswC3QME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NREhERQWMz4BNwRGARL+qv7uqP66YH1N8HGm00bXher+2QETs46RwA4FmvvO/v7+wX1kxU1jvZWuanUBASzxAkD924qwAcGbAAIAk/8nBVgHZgADACMAAAEHIxMBIREQACEiJCc3HgEzMjY9AQ4BByIANREhERQWMz4BNwRC/uLZAQsBEv6q/u6o/rpgfU3wcabTRteF6v7ZAROzjpHADgb+0wE7/jT7zv7+/sF9ZMVNY72Vrmp1AQEs8QJA/duKsAHBmwACAJP/JwVYB1wABgAmAAABByMTMxMjFyEREAAhIiQnNx4BMzI2PQEOAQciADURIREUFjM+ATcC9n3R2ena0dMBEv6q/u6o/rpgfU3wcabTRteF6v7ZAROzjpHADgbnsgEn/tmb+87+/v7BfWTFTWO9la5qdQEBLPECQP3birABwZsAAAADAJP/JwVYB1oACwAXADcAAAEiJjU0NjMyFhUUBiEiJjU0NjMyFhUUBhchERAAISIkJzceATMyNj0BDgEHIgA1ESERFBYzPgE3Aj06S0o7OkxNATw6TUw7OE1NXAES/qr+7qj+umB9TfBxptNG14Xq/tkBE7OOkcAOBkZOOz1OTj06T047PU5OPTpPrPvO/v7+wX1kxU1jvZWuanUBASzxAkD924qwAcGbAAAAAgCT/ycFWAdkAAgAKAAAACImNTQ2MhYUFyEREAAhIiQnNx4BMzI2PQEOAQciADURIREUFjM+ATcDNX5RUX5QwQES/qr+7qj+umB9TfBxptNG14Xq/tkBE7OOkcAOBj1TQUBTUoL2+87+/v7BfWTFTWO9la5qdQEBLPECQP3birABwZsAAgCT/a4FWAWaAB8AKQAAASEREAAhIiQnNx4BMzI2PQEOAQciADURIREUFjM+ATcBMhYUBiImJzQ2BEYBEv6q/u6o/rpgfU3wcabTRteF6v7ZAROzjpHADv7APlJSfE8CUAWa+87+/v7BfWTFTWO9la5qdQEBLPECQP3birABwZv7OVN+UlFAP1MAAAACAJP/JwVYB2gAAwAjAAABIyclASEREAAhIiQnNx4BMzI2PQEOAQciADURIREUFjM+ATcDgeH+AQYBngES/qr+7qj+umB9TfBxptNG14Xq/tkBE7OOkcAOBi3TaP4y+87+/v7BfWTFTWO9la5qdQEBLPECQP3birABwZsAAgCT/ycFWAeRABMAMwAAASIGByc+ATMyFhUUBgcnPgE1NCYBIREQACEiJCc3HgEzMjY9AQ4BByIANREhERQWMz4BNwLyLDQFfQ18W2F2QD97OzosASwBEv6q/u6o/rpgfU3wcabTRteF6v7ZAROzjpHADgcQMCgrUlxwVj1kNikvSyojLP6K+87+/v7BfWTFTWO9la5qdQEBLPECQP3birABwZsAAAAAAgCT/ycFWAcMAAMAIwAAARUhNQEhERAAISIkJzceATMyNj0BDgEHIgA1ESERFBYzPgE3BAD95wJfARL+qv7uqP66YH1N8HGm00bXher+2QETs46RwA4HDMDA/o77zv7+/sF9ZMVNY72Vrmp1AQEs8QJA/duKsAHBmwAAAAIAk/8nBVgHcQATADMAAAEiFSMQMzIeAjMyNTMQIyIuAgEhERAAISIkJzceATMyNj0BDgEHIgA1ESERFBYzPgE3ApZKqMInTDE5FUqowypMLzYBmgES/qr+7qj+umB9TfBxptNG14Xq/tkBE7OOkcAOBrJgAR8iKSJg/uQhKCH+6PvO/v7+wX1kxU1jvZWuanUBASzxAkD924qwAcGbAAABAEgAAAUQBZoAEQAAJSEVITUBIzUhASE3IRUBMxUhAboDVvs4AWjhAY8BNfzNAgSk/pbV/nvw8MMBqs4Bb/DB/lLRAAIASAAABRAHZgADABUAAAEHIxMBIRUhNQEjNSEBITchFQEzFSEEAP7h2f7AA1b7OAFo4QGPATX8zQIEpP6W1f57Bv7TATv5ivDDAarOAW/wwf5S0QAAAAACAEgAAAUQB1wABgAYAAABIwMzFzczASEVITUBIzUhASE3IRUBMxUhAy3+5eOBgeP9qANW+zgBaOEBjwE1/M0CBKT+ltX+ewYhATu8vPmU8MMBqs4Bb/DB/lLRAAIASAAABRAHZgAIABoAAAAiJjQ2MhYVFAEhFSE1ASM1IQEhNyEVATMVIQLzflBQflH+dgNW+zgBaOEBjwE1/M0CBKT+ltX+ewY/U4JSU0BB+l7wwwGqzgFv8MH+UtEAAAIASP5zBRAFmgARABkAACUhFSE1ASM1IQEhNyEVATMVIQIyFhQGIiY0AboDVvs4AWjhAY8BNfzNAgSk/pbV/nt/flBQflDw8MMBqs4Bb/DB/lLR/UBTglJSggAAAgBC//QExQRVAA4AGwAAASERITUGIyIANTQANzYXATI2NzUuASMiBgceAQO6AQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrBEj7uK66ATj7+AEyAQO9/TuihU6ForuTlLoAAAMAQv/0BMUGFAADABIAHwAAAQcjGwEhESE1BiMiADU0ADc2FwEyNjc1LgEjIgYHHgED/P7h2cQBC/71evTq/uABGub7ff7LgKkMDKmAiK0CAqsFrNMBO/40+7iuugE4+/gBMgEDvf07ooVOhaK7k5S6AAAAAwBC//QExQYGAAsAGgAnAAAAICYnMx4BMjY3MwYDIREhNQYjIgA1NAA3NhcBMjY3NS4BIyIGBx4BAzL+/KcEsANGaEYDsAQfAQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrBNeniDlKSjmI/sr7uK66ATj7+AEyAQO9/TuihU6ForuTlLoAAAQAQv/0BMUHUgADAA8AHgArAAABByMTEiImJzMeATI2NzMGAyERITUGIyIANTQANzYXATI2NzUuASMiBgceAQQb9NXRB/SfBKQDQ2ZDA6QEDwEL/vV69Or+4AEa5vt9/suAqQwMqYCIrQICqwbuxQEp/YWafTZFRTZ9/tf7uK66ATj7+AEyAQO9/TuihU6ForuTlLoAAAAABABC/nEExQXnAAsAGgAnADEAAAAiJiczHgEyNjczBgMhESE1BiMiADU0ADc2FwEyNjc1LgEjIgYHHgETMhYUBiImJzQ2Ayr0nwSkA0NmQwOkBA8BC/71evTq/uABGub7ff7LgKkMDKmAiK0CAquvPlFRfE8CUATTmHw1Q0M1fP7d+7iuugE4+/gBMgEDvf07ooVOhaK7k5S6/sBSflJRQD9SAAAEAEL/9ATFB1IAAwAPAB4AKwAAASMnNxIyNjczDgEiJiczFgEhESE1BiMiADU0ADc2FwEyNjc1LgEjIgYHHgEDENfz90BmQwOkBJ/0nwSkAwGAAQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrBivFYv4hRTZ9mpp9Nv6Q+7iuugE4+/gBMgEDvf07ooVOhaK7k5S6AAAAAAQAQv/0BMUHlgATAB8ALgA7AAABIgYHJz4BMzIWFRQGByc+ATU0JhIiJiczHgEyNjczBgMhESE1BiMiADU0ADc2FwEyNjc1LgEjIgYHHgECvCoyBncNeFlecD4/djs5LEb0nwSkA0NmQwOkBA8BC/71evTq/uABGub7ff7LgKkMDKmAiK0CAqsHHS8pKU9ZalM+YzYnL0wrIiz9upp9NkVFNn3+1/u4rroBOPv4ATIBA739O6KFToWiu5OUugAAAAQAQv/0BMUHLwAUACAALwA8AAABIgYVIxAzMh4CMzI1MxAjIi4CEjI2NzMOASImJzMWASERITUGIyIANTQANzYXATI2NzUuASMiBgceAQJOIiSeuyVILjYWRpy3KEotNRpmQwOkBJ/0nwSkAwGAAQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrBocwLAEEICYgWv7+ICYg/uxFNn2amn02/pD7uK66ATj7+AEyAQO9/TuihU6ForuTlLoAAwBC//QExQYKAAYAFQAiAAABByMTMxMjFyERITUGIyIANTQANzYXATI2NzUuASMiBgceAQKwfdHZ6tnRjQEL/vV69Or+4AEa5vt9/suAqQwMqYCIrQICqwWWswEn/tmb+7iuugE4+/gBMgEDvf07ooVOhaK7k5S6AAAAAAQAQv/0BUQG0wADAAoAGQAmAAABByMTAQcjEzMTIxM1IREhNQYjIgA1NAA3NgMyNjc1LgEjIgYHHgEFRPS20P5GecbR3dHHkQEL/vV69Or+4AEa5vu4gKkMDKmAiK0CAqsGccUBJ/64qgEX/un+t7D7uK66ATj7+AEyAQP8fqKFToWiu5OUugAEAEL+cQTFBfIABgAVACIALAAAAQcjEzMTIxchESE1BiMiADU0ADc2FwEyNjc1LgEjIgYHHgETMhYUBiImJzQ2ArB5xtHd0ceRAQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrrz5RUXxPAlAFh6oBFf7rlfu4rroBOPv4ATIBA739O6KFToWiu5OUuv7AUn5SUUA/UgAAAAQAQv/0BMUG0wADAAoAGQAmAAABIyc3AQcjEzMTIxchESE1BiMiADU0ADc2FwEyNjc1LgEjIgYHHgEEnrf12f7lecbR3dHHkQEL/vV69Or+4AEa5vt9/suAqQwMqYCIrQICqwWsxWL+uKoBF/7pmfu4rroBOPv4ATIBA739O6KFToWiu5OUugAEAEL/9ATFByUAEwAaACkANgAAASIGByc+ATMyFhUUBgcnPgE1NCYBByMTMxMjFyERITUGIyIANTQANzYXATI2NzUuASMiBgceAQO2KjIGdw13WV5xPj92Ozks/tJ5xtHd0ceRAQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrBqwvKSlPWWpTPmM2Jy9MKyIs/t+qARf+6Zn7uK66ATj7+AEyAQO9/TuihU6ForuTlLoAAAAEAEL/9ATFBy8AEwAaACkANgAAASIGFSMQMzIeATMyNTMQIyIuAgczEyMnByMFIREhNQYjIgA1NAA3NhcBMjY3NS4BIyIGBx4BAkgiJJy5L1hHGUibuChJLTUc3dHHeXnGAkkBC/71evTq/uABGub7ff7LgKkMDKmAiK0CAqsGhS8rAQQ0NFz+/h8mH43+6aqqmfu4rroBOPv4ATIBA739O6KFToWiu5OUugAABABC//QExQYhAAMABwAWACMAAAEjJzcBIyc3EyERITUGIyIANTQANzYXATI2NzUuASMiBgceAQJ9y7zdAgzLvN2FAQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrBNnqXv646l7+J/u4rroBOPv4ATIBA739O6KFToWiu5OUugAEAEL/9ATFBggACwAXACYAMwAAASImNTQ2MzIWFRQGISImNTQ2MzIWFRQGFyERITUGIyIANTQANzYXATI2NzUuASMiBgceAQH4OktKOzlMTAE8Ok5NOzhNTRUBC/71evTq/uABGub7ff7LgKkMDKmAiK0CAqsE9E47PU5OPTtOTjs9Tk49Ok+s+7iuugE4+/gBMgEDvf07ooVOhaK7k5S6AAAAAAMAQv5xBMUEVQAOABsAJQAAASERITUGIyIANTQANzYXATI2NzUuASMiBgceARMyFhQGIiYnNDYDugEL/vV69Or+4AEa5vt9/suAqQwMqYCIrQICq68+UVF8TwJQBEj7uK66ATj7+AEyAQO9/TuihU6ForuTlLr+wFJ+UlFAP1IAAwBC//QExQYXAAMAEgAfAAABIyclASERITUGIyIANTQANzYXATI2NzUuASMiBgceAQM74f4BBgFYAQv+9Xr06v7gARrm+33+y4CpDAypgIitAgKrBNvTaf4x+7iuugE4+/gBMgEDvf07ooVOhaK7k5S6AAADAEL/9ATFBj8AEwAiAC8AAAEiBgcnPgEzMhYVFAYHJz4BNTQmEyERITUGIyIANTQANzYXATI2NzUuASMiBgceAQKsKzQFfQ17W2F2QD97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AAL+ugTXAT0HLwAUACAAAAEQIyIuAiMiBhUjEDMyHgIzMjUBHgEyNjczDgEiJicBPbYoSi01FSIknrslSC42Fkb+5QNDZkMDpASf9J8EByP+/iAmIDAsAQQgJiBa/ss2RUU2fZqafQAAAv7BBOECkwbTAAMACgAAARcHIyUzEyMnByMButnzt/6o3tDGeXnGBtNixUz+6aqqAAAAAAL+wQThAe4G0wADAAoAAAETIycHMxMjJwcjARvTt/Wx3tDGeXnGBtP+2cV5/umqqgAAAAAC/sEE4QHZByUAEwAaAAATPgEzMhYVFAYHJz4BNTQmIyIGBwUzEyMnByMtDXdZXnE+P3c7OiwoKjIG/u3e0MZ5ecYGfU9ZalM+YzYnL0wrIiwvKVz+6aqqAAAAAAL+tgThAT8HLwATABoAAAEQIyIuAiMiBhUjEDMyHgEzMjUBMxMjJwcjATm4KEktNRYiJJy5L1hHGUj+897Qxnl5xgcj/v4fJh8vKwEENDRc/tX+6aqqAAAAAB4BbgABAAAAAAAAAFIApgABAAAAAAABABEBHQABAAAAAAACAAcBPwABAAAAAAADABwBgQABAAAAAAAEABEBwgABAAAAAAAFADwCTgABAAAAAAAGABECrwABAAAAAAAIABEC5QABAAAAAAAJABEDGwABAAAAAAALABkDYQABAAAAAAAMABkDrwABAAAAAAANAJAE6wABAAAAAAAOABoFsgABAAAAAAAQAAoF4wABAAAAAAARAAYF/AADAAEECQAAAKQAAAADAAEECQABACIA+QADAAEECQACAA4BLwADAAEECQADADgBRwADAAEECQAEACIBngADAAEECQAFAHgB1AADAAEECQAGACICiwADAAEECQAIACICwQADAAEECQAJACIC9wADAAEECQALADIDLQADAAEECQAMADIDewADAAEECQANASADyQADAAEECQAOADQFfAADAAEECQAQABQFzQADAAEECQARAAwF7gBDAG8AcAB5AHIAaQBnAGgAdAAgADIAMAAxADEAIAAtACAAMgAwADEANgAgAFQAaABlACAATQBvAG4AdABzAGUAcgByAGEAdAAgAFAAcgBvAGoAZQBjAHQAIABBAHUAdABoAG8AcgBzACAAKABqAHUAbABpAGUAdABhAC4AdQBsAGEAbgBvAHYAcwBrAHkAQABn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AH0AfAC4AKEAfwB+AIAAgQDsAO4AugEKAQsBDAENAQ4BDwD9AP4BEAERARIBEwD/AQABFAEVARYBAQEXARgBGQEaARsBHAEdAR4BHwEgASEBIgD4APkBIwEkASUBJgEnASgBKQEqASsBLAEtAS4BLwEwATEBMgD6ANcBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEA4gDjAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQALAAsQFRAVIBUwFUAVUBVgFXAVgBWQFaAPsA/ADkAOUBWwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8BcAC7AXEBcgFzAXQA5gDnAXUApgF2AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCAYMBhAGFAYYBhwGIAYkBigGLAYwBjQGOAY8BkAGRAZIBkwGUAZUBlgGXAZgBmQGaAZsBnAGdAZ4BnwGgAaEBogGjAaQBpQGmAacBqAGpAaoBqwGsAa0BrgGvAbABsQGyAbMBtAG1AbYA2ADhAbcBuAG5AboBuwDbANwA3QDgANkA3wG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBxwHIAckBygHLAcwBzQHOAc8B0AHRAdIB0wHUAdUB1gCbAdcB2AHZAdoB2wHcAd0B3gHfAeAB4QHiAeMB5AHlAeYB5wHoAekB6gHrAewB7QHuAe8B8AHxAfIB8wH0AfUB9gH3AfgB+QH6AfsB/AH9Af4B/wIAAgECAgIDAgQCBQIGAgcCCAIJAgoCCwIMAg0CDgIPAhACEQISAhMCFAIVAhYCFwIYAhkCGgIbAhwCHQIeAh8CIAIhAiICIwIkAiUCJgInAigCKQIqAisCLAItAi4CLwIwAjECMgIzAjQCNQI2AjcCOAI5AjoCOwI8Aj0CPgI/AkACQQJCAkMCRAJFAkYCRwJIAkkCSgJLAkwCTQJOAk8CUAJRAlICUwJUAlUCVgJXAlgCWQJaAlsCXAJdAl4CXwJgAmECYgJjAmQCZQJmAmcCaAJpAmoCawJsAm0CbgJvAnACcQJyAnMCdAJ1AnYCdwJ4AnkCegJ7AnwCfQJ+An8CgAKBALIAswKCALYAtwDEALQAtQDFAIIAwgCHAKsAxgKDAoQAvgC/ALwChQKGAocCiAKJAooCiwKMAo0CjgKPApACkQKSApMClAKVApYA9wKXApgCmQKaApsCnAKdAp4CnwKgAqECogKjAqQCpQKmAIwCpwKoAqkCqgKrAqwAmAKtAq4AmgCZAO8CrwKwAKUAkgCcAKcAjwCUAJUAuQDAAMECsQKyArMCtAK1ArYCtwK4ArkCugK7ArwCvQK+Ar8CwALBAsICwwLEAsUCxgLHAsgCyQLKAssCzALNAs4CzwLQAtEC0gLTAtQC1QLWAtcC2ALZAtoC2wLcAt0C3gLfAuAC4QLiAuMC5ALlAuYC5wLoAukC6gLrAuwC7QLuAu8C8ALxAvIC8wL0AvUC9gL3AvgC+QL6AvsC/AL9Av4C/wMAAwEDAgMDAwQDBQMGAwcDCAMJAwoDCwMMAw0DDgMPAxADEQMSAxMDFAMVAxYDFwMYAxkDGgMbAxwDHQMeAx8DIAMhAyIDIwMkAyUDJgMnAygDKQMqAysDLAMtAy4DLwMwAzEDMgMzAzQDNQM2AzcDOAM5AzoDOwM8Az0DPgM/A0ADQQNCA0MDRANFA0YDRwNIA0kDSgNLA0wDTQNOA08DUANRA1IDUwNUA1UDVgNXA1gDWQNaA1sDXANdA14DXwNgA2EDYgNjA2QDZQNmA2cDaANpA2oDawNsA20DbgNvA3ADcQNyA3MDdAN1A3YDdwN4A3kDegN7A3wDfQN+A38DgAOBA4IDgwOEA4UDhgOHA4gDiQOKA4sDjAONA44DjwOQA5EDkgOTA5QDlQOWA5cDmAOZA5oDmwOcA50DngOfA6ADoQOiA6MDpAOlA6YDpwOoA6kDqgOrA6wDrQOuA68DsAOxA7IDswO0A7UDtgO3A7gDuQO6A7sDvAO9A74DvwPAA8EDwgPDA8QDxQPGA8cDyAPJA8oDywPMA80DzgPPA9AD0QPSA9MD1APVA9YD1wPYA9kD2gPbA9wD3QPeA98D4APhA+ID4wPkA+UD5gPnA+gETlVMTAJDUgd1bmkwMEEwB3VuaTAwQUQHdW5pMDBCMgd1bmkwMEIzB3VuaTAwQjUHdW5pMDBCOQdBbWFjcm9uB2FtYWNyb24GQWJyZXZlBmFicmV2ZQdBb2dvbmVrB2FvZ29uZWsLQ2NpcmN1bWZsZXgLY2NpcmN1bWZsZXgKQ2RvdGFjY2VudApjZG90YWNjZW50BkRjYXJvbgZkY2Fyb24GRGNyb2F0B0VtYWNyb24HZW1hY3JvbgZFYnJldmUGZWJyZXZlCkVkb3RhY2NlbnQKZWRvdGFjY2VudAdFb2dvbmVrB2VvZ29uZWsGRWNhcm9uBmVjYXJvbgtHY2lyY3VtZmxleAtnY2lyY3VtZmxleApHZG90YWNjZW50Cmdkb3RhY2NlbnQMR2NvbW1hYWNjZW50DGdjb21tYWFjY2VudAtIY2lyY3VtZmxleAtoY2lyY3VtZmxleARIYmFyBGhiYXIGSXRpbGRlBml0aWxkZQdJbWFjcm9uB2ltYWNyb24GSWJyZXZlBmlicmV2ZQdJb2dvbmVrB2lvZ29uZWsCSUoCaWoLSmNpcmN1bWZsZXgLamNpcmN1bWZsZXgMS2NvbW1hYWNjZW50DGtjb21tYWFjY2VudAxrZ3JlZW5sYW5kaWMGTGFjdXRlBmxhY3V0ZQxMY29tbWFhY2NlbnQMbGNvbW1hYWNjZW50BkxjYXJvbgZsY2Fyb24ETGRvdARsZG90Bk5hY3V0ZQZuYWN1dGUMTmNvbW1hYWNjZW50DG5jb21tYWFjY2VudAZOY2Fyb24GbmNhcm9uC25hcG9zdHJvcGhlA0VuZwNlbmcHT21hY3JvbgdvbWFjcm9uBk9icmV2ZQZvYnJldmUNT2h1bmdhcnVtbGF1dA1vaHVuZ2FydW1sYXV0BlJhY3V0ZQZyYWN1dGUMUmNvbW1hYWNjZW50DHJjb21tYWFjY2VudAZSY2Fyb24GcmNhcm9uBlNhY3V0ZQZzYWN1dGULU2NpcmN1bWZsZXgLc2NpcmN1bWZsZXgHdW5pMDE2Mgd1bmkwMTYzBlRjYXJvbgZ0Y2Fyb24EVGJhcgR0YmFyBlV0aWxkZQZ1dGlsZGUHVW1hY3Jvbgd1bWFjcm9uBlVicmV2ZQZ1YnJldmUFVXJpbmcFdXJpbmcNVWh1bmdhcnVtbGF1dA11aHVuZ2FydW1sYXV0B1VvZ29uZWsHdW9nb25lawtXY2lyY3VtZmxleAt3Y2lyY3VtZmxleAtZY2lyY3VtZmxleAt5Y2lyY3VtZmxleAZaYWN1dGUGemFjdXRlClpkb3RhY2NlbnQKemRvdGFjY2VudAd1bmkwMThGBU9ob3JuBW9ob3JuBVVob3JuBXVob3JuB3VuaTAxQzQHdW5pMDFDNQd1bmkwMUM2B3VuaTAxQzcHdW5pMDFDOAd1bmkwMUM5B3VuaTAxQ0EHdW5pMDFDQgd1bmkwMUNDBkdjYXJvbgZnY2Fyb24HdW5pMDFFQQd1bmkwMUVCCkFyaW5nYWN1dGUKYXJpbmdhY3V0ZQdBRWFjdXRlB2FlYWN1dGULT3NsYXNoYWN1dGULb3NsYXNoYWN1dGUHdW5pMDIwMAd1bmkwMjAxB3VuaTAyMDIHdW5pMDIwMwd1bmkwMjA0B3VuaTAyMDUHdW5pMDIwNgd1bmkwMjA3B3VuaTAyMDgHdW5pMDIwOQd1bmkwMjBBB3VuaTAyMEIHdW5pMDIwQwd1bmkwMjBEB3VuaTAyMEUHdW5pMDIwRgd1bmkwMjEwB3VuaTAyMTEHdW5pMDIxMgd1bmkwMjEzB3VuaTAyMTQHdW5pMDIxNQd1bmkwMjE2B3VuaTAyMTcMU2NvbW1hYWNjZW50DHNjb21tYWFjY2VudAd1bmkwMjFBB3VuaTAyMUIHdW5pMDIyQQd1bmkwMjJCB3VuaTAyMkMHdW5pMDIyRAd1bmkwMjMwB3VuaTAyMzEHdW5pMDIzMgd1bmkwMjMzB3VuaTAyMzcHdW5pMDI1OQd1bmkwMkJCB3VuaTAyQkMHdW5pMDJCRQd1bmkwMkJGB3VuaTAyQzgHdW5pMDJDOQd1bmkwMkNBB3VuaTAyQ0IHdW5pMDJDQwlncmF2ZWNvbWIJYWN1dGVjb21iB3VuaTAzMDIJdGlsZGVjb21iB3VuaTAzMDQHdW5pMDMwNgd1bmkwMzA3B3VuaTAzMDgNaG9va2Fib3ZlY29tYgd1bmkwMzBBB3VuaTAzMEIHdW5pMDMwQwd1bmkwMzBGB3VuaTAzMTEHdW5pMDMxMgd1bmkwMzFCDGRvdGJlbG93Y29tYgd1bmkwMzI0B3VuaTAzMjYHdW5pMDMyNwd1bmkwMzI4B3VuaTAzMkUHdW5pMDMzMQd1bmkwMzM1B3VuaTAzOTQHdW5pMDNBOQd1bmkwM0JDB3VuaTFFMDgHdW5pMUUwOQd1bmkxRTBDB3VuaTFFMEQHdW5pMUUwRQd1bmkxRTBGB3VuaTFFMTQHdW5pMUUxNQd1bmkxRTE2B3VuaTFFMTcHdW5pMUUxQwd1bmkxRTFEB3VuaTFFMjAHdW5pMUUyMQd1bmkxRTI0B3VuaTFFMjUHdW5pMUUyQQd1bmkxRTJCB3VuaTFFMkUHdW5pMUUyRgd1bmkxRTM2B3VuaTFFMzcHdW5pMUUzQQd1bmkxRTNCB3VuaTFFNDIHdW5pMUU0Mwd1bmkxRTQ0B3VuaTFFNDUHdW5pMUU0Ngd1bmkxRTQ3B3VuaTFFNDgHdW5pMUU0OQd1bmkxRTRDB3VuaTFFNEQHdW5pMUU0RQd1bmkxRTRGB3VuaTFFNTAHdW5pMUU1MQd1bmkxRTUyB3VuaTFFNTMHdW5pMUU1QQd1bmkxRTVCB3VuaTFFNUUHdW5pMUU1Rgd1bmkxRTYwB3VuaTFFNjEHdW5pMUU2Mgd1bmkxRTYzB3VuaTFFNjQHdW5pMUU2NQd1bmkxRTY2B3VuaTFFNjcHdW5pMUU2OAd1bmkxRTY5B3VuaTFFNkMHdW5pMUU2RAd1bmkxRTZFB3VuaTFFNkYHdW5pMUU3OAd1bmkxRTc5B3VuaTFFN0EHdW5pMUU3QgZXZ3JhdmUGd2dyYXZlBldhY3V0ZQZ3YWN1dGUJV2RpZXJlc2lzCXdkaWVyZXNpcwd1bmkxRThFB3VuaTFFOEYHdW5pMUU5Mgd1bmkxRTkzB3VuaTFFOTcHdW5pMUU5RQd1bmkxRUEwB3VuaTFFQTEHdW5pMUVBMgd1bmkxRUEzB3VuaTFFQTQHdW5pMUVBNQd1bmkxRUE2B3VuaTFFQTcHdW5pMUVBOAd1bmkxRUE5B3VuaTFFQUEHdW5pMUVBQgd1bmkxRUFDB3VuaTFFQUQHdW5pMUVBRQd1bmkxRUFGB3VuaTFFQjAHdW5pMUVCMQd1bmkxRUIyB3VuaTFFQjMHdW5pMUVCNAd1bmkxRUI1B3VuaTFFQjYHdW5pMUVCNwd1bmkxRUI4B3VuaTFFQjkHdW5pMUVCQQd1bmkxRUJCB3VuaTFFQkMHdW5pMUVCRAd1bmkxRUJFB3VuaTFFQkYHdW5pMUVDMAd1bmkxRUMxB3VuaTFFQzIHdW5pMUVDMwd1bmkxRUM0B3VuaTFFQzUHdW5pMUVDNgd1bmkxRUM3B3VuaTFFQzgHdW5pMUVDOQd1bmkxRUNBB3VuaTFFQ0IHdW5pMUVDQwd1bmkxRUNEB3VuaTFFQ0UHdW5pMUVDRgd1bmkxRUQwB3VuaTFFRDEHdW5pMUVEMgd1bmkxRUQzB3VuaTFFRDQHdW5pMUVENQd1bmkxRUQ2B3VuaTFFRDcHdW5pMUVEOAd1bmkxRUQ5B3VuaTFFREEHdW5pMUVEQgd1bmkxRURDB3VuaTFFREQHdW5pMUVERQd1bmkxRURGB3VuaTFFRTAHdW5pMUVFMQd1bmkxRUUyB3VuaTFFRTMHdW5pMUVFNAd1bmkxRUU1B3VuaTFFRTYHdW5pMUVFNwd1bmkxRUU4B3VuaTFFRTkHdW5pMUVFQQd1bmkxRUVCB3VuaTFFRUMHdW5pMUVFRAd1bmkxRUVFB3VuaTFFRUYHdW5pMUVGMAd1bmkxRUYxBllncmF2ZQZ5Z3JhdmUHdW5pMUVGNAd1bmkxRUY1B3VuaTFFRjYHdW5pMUVGNwd1bmkxRUY4B3VuaTFFRjkHdW5pMjAwNwd1bmkyMDA4B3VuaTIwMDkHdW5pMjAwQQd1bmkyMDBCB3VuaTIwMTAKZmlndXJlZGFzaAd1bmkyMDE1Bm1pbnV0ZQZzZWNvbmQHdW5pMjA3MAd1bmkyMDc0B3VuaTIwNzUHdW5pMjA3Ngd1bmkyMDc3B3VuaTIwNzgHdW5pMjA3OQd1bmkyMDgwB3VuaTIwODEHdW5pMjA4Mgd1bmkyMDgzB3VuaTIwODQHdW5pMjA4NQd1bmkyMDg2B3VuaTIwODcHdW5pMjA4OAd1bmkyMDg5DWNvbG9ubW9uZXRhcnkEbGlyYQd1bmkyMEE2BnBlc2V0YQd1bmkyMEE5BGRvbmcERXVybwd1bmkyMEFEB3VuaTIwQjEHdW5pMjBCMgd1bmkyMEI1B3VuaTIwQjkHdW5pMjBCQQd1bmkyMEJDB3VuaTIwQkQHdW5pMjExMwd1bmkyMTE2B3VuaTIxMjYJZXN0aW1hdGVkCW9uZWVpZ2h0aAx0aHJlZWVpZ2h0aHMLZml2ZWVpZ2h0aHMMc2V2ZW5laWdodGhzCGVtcHR5c2V0B3VuaTIyMDYHdW5pMjIxNQd1bmkyMjE5BkEuc3MwMQtBYWN1dGUuc3MwMQtBYnJldmUuc3MwMQx1bmkxRUFFLnNzMDEMdW5pMUVCNi5zczAxDHVuaTFFQjAuc3MwMQx1bmkxRUIyLnNzMDEMdW5pMUVCNC5zczAxEEFjaXJjdW1mbGV4LnNzMDEMdW5pMUVBNC5zczAxDHVuaTFFQUMuc3MwMQx1bmkxRUE2LnNzMDEMdW5pMUVBOC5zczAxDHVuaTFFQUEuc3MwMQx1bmkwMjAwLnNzMDEOQWRpZXJlc2lzLnNzMDEMdW5pMUVBMC5zczAxC0FncmF2ZS5zczAxDHVuaTFFQTIuc3MwMQx1bmkwMjAyLnNzMDEMQW1hY3Jvbi5zczAxDEFvZ29uZWsuc3MwMQpBcmluZy5zczAxD0FyaW5nYWN1dGUuc3MwMQtBdGlsZGUuc3MwMQx1bmkwMUM0LnNzMDEMdW5pMDFDNS5zczAxBkUuc3MwMQtFYWN1dGUuc3MwMQtFYnJldmUuc3MwMQtFY2Fyb24uc3MwMQx1bmkxRTFDLnNzMDEQRWNpcmN1bWZsZXguc3MwMQx1bmkxRUJFLnNzMDEMdW5pMUVDNi5zczAxDHVuaTFFQzAuc3MwMQx1bmkxRUMyLnNzMDEMdW5pMUVDNC5zczAxDHVuaTAyMDQuc3MwMQ5FZGllcmVzaXMuc3MwMQ9FZG90YWNjZW50LnNzMDEMdW5pMUVCOC5zczAxC0VncmF2ZS5zczAxDHVuaTFFQkEuc3MwMQx1bmkwMjA2LnNzMDEMRW1hY3Jvbi5zczAxDHVuaTFFMTYuc3MwMQx1bmkxRTE0LnNzMDEMRW9nb25lay5zczAxDHVuaTFFQkMuc3MwMQZGLnNzMDEGRy5zczAxC0dicmV2ZS5zczAxC0djYXJvbi5zczAxEEdjaXJjdW1mbGV4LnNzMDERR2NvbW1hYWNjZW50LnNzMDEPR2RvdGFjY2VudC5zczAxDHVuaTFFMjAuc3MwMQZJLnNzMDEHSUouc3MwMQtJYWN1dGUuc3MwMRBJYWN1dGVfSi5sb2NsTkxEFUlhY3V0ZV9KLmxvY2xOTEQuc3MwMQtJYnJldmUuc3MwMRBJY2lyY3VtZmxleC5zczAxDHVuaTAyMDguc3MwMQ5JZGllcmVzaXMuc3MwMQx1bmkxRTJFLnNzMDEPSWRvdGFjY2VudC5zczAxDHVuaTFFQ0Euc3MwMQtJZ3JhdmUuc3MwMQx1bmkxRUM4LnNzMDEMdW5pMDIwQS5zczAxDEltYWNyb24uc3MwMQxJb2dvbmVrLnNzMDELSXRpbGRlLnNzMDEGSi5zczAxEEpjaXJjdW1mbGV4LnNzMDEMdW5pMDFDNy5zczAxBk0uc3MwMQx1bmkxRTQyLnNzMDEGTi5zczAxDHVuaTAxQ0Euc3MwMQtOYWN1dGUuc3MwMQtOY2Fyb24uc3MwMRFOY29tbWFhY2NlbnQuc3MwMQx1bmkxRTQ0LnNzMDEMdW5pMUU0Ni5zczAxCEVuZy5zczAxDHVuaTAxQ0Iuc3MwMQx1bmkxRTQ4LnNzMDELTnRpbGRlLnNzMDEGUS5zczAxDHVuaTAxOEYuc3MwMQZULnNzMDEJVGJhci5zczAxC1RjYXJvbi5zczAxDHVuaTAxNjIuc3MwMQx1bmkwMjFBLnNzMDEMdW5pMUU2Qy5zczAxDHVuaTFFNkUuc3MwMQZVLnNzMDELVWFjdXRlLnNzMDELVWJyZXZlLnNzMDEQVWNpcmN1bWZsZXguc3MwMQx1bmkwMjE0LnNzMDEOVWRpZXJlc2lzLnNzMDEMdW5pMUVFNC5zczAxC1VncmF2ZS5zczAxDHVuaTFFRTYuc3MwMQpVaG9ybi5zczAxDHVuaTFFRTguc3MwMQx1bmkxRUYwLnNzMDEMdW5pMUVFQS5zczAxDHVuaTFFRUMuc3MwMQx1bmkxRUVFLnNzMDESVWh1bmdhcnVtbGF1dC5zczAxDHVuaTAyMTYuc3MwMQxVbWFjcm9uLnNzMDEMdW5pMUU3QS5zczAxDFVvZ29uZWsuc3MwMQpVcmluZy5zczAxC1V0aWxkZS5zczAxDHVuaTFFNzguc3MwMQZXLnNzMDELV2FjdXRlLnNzMDEQV2NpcmN1bWZsZXguc3MwMQ5XZGllcmVzaXMuc3MwMQtXZ3JhdmUuc3MwMQZZLnNzMDELWWFjdXRlLnNzMDEQWWNpcmN1bWZsZXguc3MwMQ5ZZGllcmVzaXMuc3MwMQx1bmkxRThFLnNzMDEMdW5pMUVGNC5zczAxC1lncmF2ZS5zczAxDHVuaTFFRjYuc3MwMQx1bmkwMjMyLnNzMDEMdW5pMUVGOC5zczAxBlouc3MwMQtaYWN1dGUuc3MwMQtaY2Fyb24uc3MwMQ9aZG90YWNjZW50LnNzMDEMdW5pMUU5Mi5zczAxBmEuc3MwMQthYWN1dGUuc3MwMQthYnJldmUuc3MwMQx1bmkxRUFGLnNzMDEMdW5pMUVCNy5zczAxDHVuaTFFQjEuc3MwMQx1bmkxRUIzLnNzMDEMdW5pMUVCNS5zczAxEGFjaXJjdW1mbGV4LnNzMDEMdW5pMUVBNS5zczAxDHVuaTFFQUQuc3MwMQx1bmkxRUE3LnNzMDEMdW5pMUVBOS5zczAxDHVuaTFFQUIuc3MwMQx1bmkwMjAxLnNzMDEOYWRpZXJlc2lzLnNzMDEMdW5pMUVBMS5zczAxC2FncmF2ZS5zczAxDHVuaTFFQTMuc3MwMQx1bmkwMjAzLnNzMDEMYW1hY3Jvbi5zczAxDGFvZ29uZWsuc3MwMQphcmluZy5zczAxD2FyaW5nYWN1dGUuc3MwMQthdGlsZGUuc3MwMQdhZS5zczAxDGFlYWN1dGUuc3MwMQx1bmkwMUM2LnNzMDEGZS5zczAxC2VhY3V0ZS5zczAxC2VicmV2ZS5zczAxC2VjYXJvbi5zczAxDHVuaTFFMUQuc3MwMRBlY2lyY3VtZmxleC5zczAxDHVuaTFFQkYuc3MwMQx1bmkxRUM3LnNzMDEMdW5pMUVDMS5zczAxDHVuaTFFQzMuc3MwMQx1bmkxRUM1LnNzMDEMdW5pMDIwNS5zczAxDmVkaWVyZXNpcy5zczAxD2Vkb3RhY2NlbnQuc3MwMQx1bmkxRUI5LnNzMDELZWdyYXZlLnNzMDEMdW5pMUVCQi5zczAxDHVuaTAyMDcuc3MwMQxlbWFjcm9uLnNzMDEMdW5pMUUxNy5zczAxDHVuaTFFMTUuc3MwMQxlb2dvbmVrLnNzMDEMdW5pMUVCRC5zczAxDHVuaTAyNTkuc3MwMQZmLnNzMDEQaWFjdXRlX2oubG9jbE5MRAlpLmxvY2xUUksGbC5zczAxC2xhY3V0ZS5zczAxC2xjYXJvbi5zczAxEWxjb21tYWFjY2VudC5zczAxCWxkb3Quc3MwMQx1bmkxRTM3LnNzMDEMdW5pMDFDOS5zczAxDHVuaTFFM0Iuc3MwMQtsc2xhc2guc3MwMQdvZS5zczAxBnQuc3MwMQl0YmFyLnNzMDELdGNhcm9uLnNzMDEMdW5pMDE2My5zczAxDHVuaTAyMUIuc3MwMQx1bmkxRTk3LnNzMDEMdW5pMUU2RC5zczAxDHVuaTFFNkYuc3MwMQZ3LnNzMDELd2FjdXRlLnNzMDEQd2NpcmN1bWZsZXguc3MwMQ53ZGllcmVzaXMuc3MwMQt3Z3JhdmUuc3MwMQZ5LnNzMDELeWFjdXRlLnNzMDEQeWNpcmN1bWZsZXguc3MwMQ55ZGllcmVzaXMuc3MwMQx1bmkxRUY1LnNzMDELeWdyYXZlLnNzMDEMdW5pMUVGNy5zczAxDHVuaTAyMzMuc3MwMQx1bmkxRUY5LnNzMDEGei5zczAxC3phY3V0ZS5zczAxC3pjYXJvbi5zczAxD3pkb3RhY2NlbnQuc3MwMQx1bmkxRTkzLnNzMDEFZl9mX2kKZl9mX2kuc3MwMQZmX2ZfaWoLZl9mX2lqLnNzMDEFZl9mX2wKZl9mX2wuc3MwMQRmX2lqCWZfaWouc3MwMQdmaS5zczAxB2ZsLnNzMDEHemVyby5sZgZvbmUubGYGdHdvLmxmCHRocmVlLmxmB2ZvdXIubGYHZml2ZS5sZgZzaXgubGYIc2V2ZW4ubGYIZWlnaHQubGYHbmluZS5sZgh6ZXJvLm9zZgdvbmUub3NmB3R3by5vc2YJdGhyZWUub3NmCGZvdXIub3NmCGZpdmUub3NmB3NpeC5vc2YJc2V2ZW4ub3NmCWVpZ2h0Lm9zZghuaW5lLm9zZgl6ZXJvLnRvc2YIb25lLnRvc2YIdHdvLnRvc2YKdGhyZWUudG9zZglmb3VyLnRvc2YJZml2ZS50b3NmCHNpeC50b3NmCnNldmVuLnRvc2YKZWlnaHQudG9zZgluaW5lLnRvc2YJemVyby5kbm9tCG9uZS5kbm9tCHR3by5kbm9tCnRocmVlLmRub20JZm91ci5kbm9tCWZpdmUuZG5vbQhzaXguZG5vbQpzZXZlbi5kbm9tCmVpZ2h0LmRub20JbmluZS5kbm9tCXplcm8ubnVtcghvbmUubnVtcgh0d28ubnVtcgp0aHJlZS5udW1yCWZvdXIubnVtcglmaXZlLm51bXIIc2l4Lm51bXIKc2V2ZW4ubnVtcgplaWdodC5udW1yCW5pbmUubnVtchZwZXJpb2RjZW50ZXJlZC5sb2NsQ0FUDWNhcm9uY29tYi5hbHQLdW5pMDMyNi5hbHQSaG9va2Fib3ZlY29tYi5jYXNlEHVuaTAzMDIubG9jbFZJRVQQdW5pMDMwNi5sb2NsVklFVBVkb3RiZWxvd2NvbWIubG9jbFZJRVQTY2Fyb25jb21iLmFsdC5zaG9ydAt1bmkwMzA2MDMwMQt1bmkwMzA2MDMwMAt1bmkwMzA2MDMwOQt1bmkwMzA2MDMwMwt1bmkwMzAyMDMwMQt1bmkwMzAyMDMwMAt1bmkwMzAyMDMwOQt1bmkwMzAyMDMwMwAAAAAAAf//AAIAAQAAAAwAAABkAHgAAgAOAAEBkgABAZMBqgADAasCpgABAqcCqAACAqkDlAABA5UDlQACA5YDlgABA5cDlwACA5gDmAABA5kDmQACA5oDmgABA5sDmwACA5wD0QABA9ID4AADAA4AAQAGAAEABAABAsEAAQABA54AAgADAZMBoQABA9MD1gABA9kD4AABAAAAAQAAAAoCIgN0AAJERkxUAA5sYXRuADoABAAAAAD//wARAAAAAQADAAQABQAGAAcAEQASABMAFAAVABYAFwAYABkAGgA6AAlBWkUgAGRDQVQgAI5DUlQgALhLQVogAOJNT0wgAQxOTEQgATZST00gAWBUQVQgAYpUUksgAbQAAP//ABIAAAABAAIAAwAEAAUABgAHABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwAIABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwAJABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwAKABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwALABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwAMABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwANABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwAOABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwAPABEAEgATABQAFQAWABcAGAAZABoAAP//ABIAAAABAAMABAAFAAYABwAQABEAEgATABQAFQAWABcAGAAZABoAG2FhbHQApGNhc2UArGNjbXAAsmRsaWcAvGRub20AwmZyYWMAyGxpZ2EA0mxudW0A2GxvY2wA3mxvY2wA5GxvY2wA6mxvY2wA8GxvY2wA9mxvY2wA/GxvY2wBAmxvY2wBCGxvY2wBDm51bXIBFG9udW0BGm9yZG4BIHBudW0BKHNhbHQBLnNpbmYBNHNzMDEBOnN1YnMBQHN1cHMBRnRudW0BTAAAAAIAAAABAAAAAQAbAAAAAwACAAMABAAAAAEAHAAAAAEAEQAAAAMAEgATABQAAAABAB0AAAABABcAAAABAA0AAAABAAYAAAABAAwAAAABAAkAAAABAAgAAAABAAUAAAABAAcAAAABAAoAAAABAAsAAAABABAAAAABABoAAAACABUAFgAAAAEAGAAAAAEAHgAAAAEAIAAAAAEAHwAAAAEADgAAAAEADwAAAAEAGQAiAEYATgBWAGQAbgB2AH4AiACQAJgAoACoALAAuADAAMgA0ADYAOAA6ADwAPoBBAEMARQBHAEkASwBNAE8AUQBTAFUAVwAAQAAAAEBHgADAAAAAQUUAAYAAAAEBvIHFgdMB2oABgAAAAIKbgqMAAQAAAABCqAABAAAAAEK7gAGAAAAAgsMCzIAAQAAAAELTgABAAAAAQtgAAEAAAABC3IAAQAAAAELdgABAAAAAQt6AAEAAAABC34AAQAAAAELggABAAAAAQuGAAEAAAABC44AAQAAAAELqgABAAAAAQuyAAEAAAABC7oAAQAAAAELvgAGAAAAAgvGC+gABgAAAAIMBAwoAAQAAAABDEIAAQAAAAEMVAABAAAAAQxcAAEAAAABDJIAAQAAAAEMwgABAAAAAQz4AAQAAAABDUoABAAAAAENZAABAAAAAQ2QAAEAAAABEU4AAQAAAAEVDAABAAAAARUUAAICAAD9AmwCxALbAtwC4wL1AvgC+gB+AwUDBwMOAyUDKgM0A1UDbQGAA3AAfgN6A4IDhwOQA9ECugKqArECwQK4Ar8C0wLFAskC0ALvAuUC6QLrAwQDFQMPAxEDEwMrA0oDOgNBA1EDSANPA1IDZANWA1oDYQOIA4oCvQNNAqsDOwK+A04C1gNnAsYDVwLRA2IC2QNqAscDWALfAt0C4QLgAvQC8gLoAvMC7QLkAvYDcQNzA3IDdAN4AvwC/gL9AwEDeQF0AXUDCQN8AwgDewMjAx8DEAMiAx0DIQMnA4QDLAOJAy0DNQORAzcDkwM2A5IDBgMXAsICwwNUAvcDdgL7AwIC3gLAA1ADUwK3A0cCvANMAs8DYALVA2YC6gLxAxIDHgMLA34DMgOOA2wD1ALYA2kC1wNoAsgDWQLiAuwDdQN3AvkC/wMAAwMDDAOAAw0DgQMkAyADKQOGAyYDgwMoA4UDLgM4A5QDfwK5A0kCuwNLArIDQgK0A0QCtQNFArYDRgKzA0MCrAM8Aq4DPgKvAz8CsANAAq0DPQLSA2MC1ANlAtoDawLKA1sCzANdAs0DXgLOA18CywNcAvAC7gMUAxYDGAMaAxsDHAMZAzADjAMvA4sDMQONAzMDjwOdA54C5wOWA5gDmgOcA70DvgO/A8ADwQPCA8MDxAPFA8YAAQD9ABQAKgArACwALgAvADIAMwA0ADYAOQA6ADwAPgA/AEoASwBPAFEAVABZAFwAXgBfAHsAhACFAIYAhwCIAIkAjACNAI4AjwCQAJEAkgCTAJUAnQCeAJ8AoAChAKQApQCmAKcAqACpAKoArACtAK4ArwDBAMMAxADFAMYAxwDIAMkA1gDXANgA2QDaANsA3ADdAN4A3wDgAOIA5ADmAOwA7gDwAPIA9AD2APgA/gEAAQIBBAEGAQcBCQELAQ4BFwEiASMBKAEpASoBKwEsAS4BMAEyATQBNgE4ATkBOgE7ATwBPQE+AT8BQAFBAUIBQwFHAUkBSgFLAUwBTgFPAVABUgFWAVcBWQFcAV0BXgFfAWABYQFiAWMBZAFmAXABcgF2AXcBfgF/AYEBmwG1AbYBtwG4AbkBugG7AcEBxAHGAccByQHLAc0B5QHmAecB6AHpAesB7QHuAe8B8AHxAfIB8wH1AfYB9wH5AfoB+wH8Af0B/gH/AgACAQICAgMCBAIFAgYCBwIIAgkCCgILAgwCDQIOAg8CEAIRAhICEwIUAhUCFgIXAhgCGQIaAhsCHAIdAh4CHwIgAiECIwI9Aj8CQQJDAkUCRwJJAksCTAJNAk4CTwJQAlECUgKnAqgC5gOVA5cDmQObA8cDyAPJA8oDywPMA80DzgPPA9AAAQG+AC0AYABuAHwAigCYAKYAtADCANAA3gDsAPIA+AD+AQQBCgEQARYBHAEiASgBLgE0AToBQAFGAUwBUgFYAV4BZAFqAXABdgF8AYIBiAGOAZQBmgGgAaYBrAGyAbgABgJ0Am0DxwO9A58DswAGAnUAfQPIA74DoAO0AAYCdgB2A8kDvwOhA7UABgJ3AHcDygPAA6IDtgAGAngCbgPLA8EDowO3AAYCeQJvA8wDwgOkA7gABgJ6AnADzQPDA6UDuQAGAnsCcQPOA8QDpgO6AAYCfAJyA88DxQOnA7sABgJ9AnMD0APGA6gDvAACAG4CqQACAG4DOQACAPUDbwACAXYDCgACAXcDfQACABUDqQACABYDqgACABcDqwACABgDrAACABkDrQACABoDrgACABsDrwACABwDsAACAB0DsQAC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AAoAAgAuABQAFQAWABcAGAAZABoAGwAcAB0AHgOzA7QDtQO2A7cDuAO5A7oDuwO8AAIAAQOfA7IAAAACAC4AFAOzA7QDtQO2A7cDuAO5A7oDuwO8A6kDqgOrA6wDrQOuA68DsAOxA7IAAgACABUAHgAAA58DqAAKAAIARAAfA58DoAOhA6IDowOkA6UDpgOnA6gD1AOfA6ADoQOiA6MDpAOlA6YDpwOoA58DoAOhA6IDowOkA6UDpgOnA6gAAgADABUAHgAAAZsBmwAKA6kDvAALAAEAHAABAAgAAgAGAA4DlwADAEsA9wObAAIA9wABAAEASwABAC4AAQAIAAQACgASABoAIAOZAAMASwBRA5UAAwBLAE4CqAACAFECpwACAE4AAQABAEsAAgHkAO8CqQLEAtsC3ALjAvUC+AL6AwUDBwMOAyUDKgM0AzkDVQNtA3ADegOCA4cDkAK6AqoCsQLBArgCvwLTAsUCyQLQAu8C5QLpAusDBAMVAw8DEQMTAysDSgM6A0EDUQNIA08DUgNkA1YDWgNhA4gDigK9A00CqwM7Ar4DTgLWA2cCxgNXAtEDYgLZA2oCxwNYAt8C3QLhAuAC9ALyAugC8wLtAuQC9gNxA3MDcgN0A3gC/AL+Av0DAQN5AwoDfQMJA3wDCAN7AyMDHwMQAyIDHQMhAycDhAMsA4kDLQM1A5EDNwOTAzYDkgMGAxcCwgLDA1QC9wN2AvsDAgLeAsADUANTArcDRwK8A0wCzwNgAtUDZgLqAvEDEgMeAwsDfgMyA44DbALYA2kC1wNoAsgDWQLiAuwDdQN3AvkC/wMAAwMDDAOAAw0DgQMkAyADKQOGAyYDgwMoA4UDLgM4A5QDfwK5A0kCuwNLArIDQgK0A0QCtQNFArYDRgKzA0MCrAM8Aq4DPgKvAz8CsANAAq0DPQLSA2MC1ANlAtoDawLKA1sCzANdAs0DXgLOA18CywNcAvAC7gMUAxYDGAMaAxsDHAMZAzADjAMvA4sDMQONAzMDjwOdA54C5wOWA5gDmgOcAAEA7wAmACoAKwAsAC4ALwAyADMANgA5ADoAPAA+AD8ARgBKAEsAUQBZAFwAXgBfAIQAhQCGAIcAiACJAIwAjQCOAI8AkACRAJIAkwCVAJ0AngCfAKAAoQCkAKUApgCnAKgAqQCqAKwArQCuAK8AwQDDAMQAxQDGAMcAyADJANYA1wDYANkA2gDbANwA3QDeAN8A4ADiAOQA5gDsAO4A8ADyAPQA9gD4AP4BAAECAQQBBgEHAQkBCwEOARcBJgEnASgBKQEqASsBLAEuATABMgE0ATYBOAE5AToBOwE8AT0BPgE/AUABQQFCAUMBRwFJAUoBSwFMAU4BTwFQAVIBVgFXAVkBXAFdAV4BXwFgAWEBYgFjAWQBZgFwAXIBdgF3AX4BfwGBAbUBtgG3AbgBuQG6AbsBwQHEAcYBxwHJAcsBzQHlAeYB5wHoAekB6wHtAe4B7wHwAfEB8gHzAfUB9gH3AfkB+gH7AfwB/QH+Af8CAAIBAgICAwIEAgUCBgIHAggCCQIKAgsCDAINAg4CDwIQAhECEgITAhQCFQIWAhcCGAIZAhoCGwIcAh0CHgIfAiACIQIjAj0CPwJBAkMCRQJHAkkCSwJMAk0CTgJPAlACUQJSAqcCqALmA5UDlwOZA5sAAgHkAO8CqQLEAtsC3ALjAvUC+AL6AwUDBwMOAyUDKgM0AzkDVQNtA3ADegOCA4cDkAK6AqoCsQLBArgCvwLTAsUCyQLQAu8C5QLpAusDBAMVAw8DEQMTAysDSgM6A0EDUQNIA08DUgNkA1YDWgNhA4gDigK9A00CqwM7Ar4DTgLWA2cCxgNXAtEDYgLZA2oCxwNYAt8C3QLhAuAC9ALyAugC8wLtAuQC9gNxA3MDcgN0A3gC/AL+Av0DAQN5AwoDfQMJA3wDCAN7AyMDHwMQAyIDHQMhAycDhAMsA4kDLQM1A5EDNwOTAzYDkgMGAxcCwgLDA1QC9wN2AvsDAgLeAsADUANTArcDRwK8A0wCzwNgAtUDZgLqAvEDEgMeAwsDfgMyA44DbALYA2kC1wNoAsgDWQLiAuwDdQN3AvkC/wMAAwMDDAOAAw0DgQMkAyADKQOGAyYDgwMoA4UDLgM4A5QDfwK5A0kCuwNLArIDQgK0A0QCtQNFArYDRgKzA0MCrAM8Aq4DPgKvAz8CsANAAq0DPQLSA2MC1ANlAtoDawLKA1sCzANdAs0DXgLOA18CywNcAvAC7gMUAxYDGAMaAxsDHAMZAzADjAMvA4sDMQONAzMDjwOdA54C5wOWA5gDmgOcAAEA7wAmACoAKwAsAC4ALwAyADMANgA5ADoAPAA+AD8ARgBKAEsAUQBZAFwAXgBfAIQAhQCG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AAIALwAvAAEA+AD4AAEAAQABApAAAQDiAM4AAwDyAAwACAAyADgAPgBEAEoAUABWAFwAYgBoAG4AdAB6AIAAhgCMAJIAmACeAKQAqgCwALYAvAABAo8AAAABApoFmgABAAAAAAABAlIAAAABAm0ESAABAAAAAAABAtUAAAABArwESAABBCUESAABAwwAAAABAwAFmgABAAAAAAABAtsAAAABAAAAAAABBVwF8AABAxsAAAABAx8FmgABAAAAAAABAwwAAAABAwAFmgABAAAAAAABAysAAAABAzsFmgABAAAAAAABAAgACQBmAHkCfgKEAogCiQKPAAIAAgGTAakAAAPSA+AAFwAmAAEAmgABAKAAAQCmAAEArAABALIAAQC4AAEAvgABAMQAAQDKAAEA0AABANYAAQDcAAEA4gABAOgAAQDuAAIA9AAAAPoAAAEAAAABBgAAAQwAAAESAAABGAAAAR4AAgEkAAEBKgABATAAAQE2AAEBPAAAAUIAAgFIAAEBTgABAVQAAQFaAAEBYAABAWYAAQFsAAEBcgABAXgAAQApBEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAf/+BEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAABZoAAQAABEgAAf/+BEgAAQAABEgAAQAABEgAAQAAAAAAAQAABZoAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAsACYAAwA8AAwAAQAIAA4AFAABAtUAAAABArwESAABBCUESAABAAEBrQACAAIBkwGpAAAD0gPgABcAJgABAJoAAQCgAAEApgABAKwAAQCyAAEAuAABAL4AAQDEAAEAygABANAAAQDWAAEA3AABAOIAAQDoAAEA7gACAPQAAAD6AAABAAAAAQYAAAEMAAABEgAAARgAAAEeAAIBJAABASoAAQEwAAEBNgABATwAAAFCAAIBSAABAU4AAQFUAAEBWgABAWAAAQFmAAEBbAABAXIAAQF4AAEAKQRIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAH//gRIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAAAAAAEAAAAAAAEAAAAAAAEAAAAAAAEAAAAAAAEAAAAAAAEAAAAAAAEAAAWaAAEAAARIAAH//gRIAAEAAARIAAEAAARIAAEAAAAAAAEAAAWaAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAE/rj7eAAM/vgAMAp4Ptg+8D8IPyA/OD9QP2g/gD+YP7A/yD/gP/hAEEAoQEBAWEBwQIhAoEC4QNBA6EEAQRhBMEFIQWBBeEGQQahBwEHYQfBCCEIgQjhCUEJoQoBCmEKwQshC4EL4QxBDKENAQ1hDcEOIQ6BDuEPQQ+hEAEQYRDBESERgRHhEkESoRMBE2ETwRQhFIEU4RVBFaEWARZhFsEXIReBF+EYQRihGQEZYRnBGiEagRrhG0EboRwBHGEcwR0hHYEd4R5BHqEfAR9hH8EgISCBIOEhQSGhIgEiYSLBIyEjgSPhJEEkoSUBJWElwSYhJoEm4SdBJ6EoAShhKMEpISmBKeEqQSqhKwErYSvBLCEsgSzhLUEtoS4BLmEuwS8hL4Ev4TBBMKExATFhMcEyITKBMuEzQTOhNAE0YTTBNSE1gTXhNkE2oTcBN2E3wTghOIE44TlBOaE6ATphOsE7ITuBO+E8QTyhPQE9YT3BPiE+gT7hP0E/oUABQGFAwUEhQYFB4UJBQqFDAUNhQ8FEIUSBROFFQUWhRgFGYUbBRyFHgUfhSEFIoUkBSWFJwUohSoFK4UtBS6FMAUxhTMFNIU2BTeFOQU6hTwFPYU/BUCFQgVDhUUFRoVIBUmFSwVMhU4FT4VRBVKFVAVVhVcFWIVaBVuFXQVehWAFYYVjBWSFZgVnhWkFaoVsBW2FbwVwhXIFc4V1BXaFeAV5hXsFfIV+BX+FgQWChYQFhYWHBYiFigWLhY0FjoWQBZGFkwWUhZYFl4WZBZqFnAWdhZ8FoIWiBaOFpQWmhagFqYWrBayFrgWvhbEFsoW0BbWFtwW4hboFu4W9Bb6FwAXBhcMFxIXGBceFyQXKhcwFzYXPBdCF0gXThdUF1oXYBdmF2wXchd4F34XhBeKF5AXlhecF6IXqBeuF7QXuhfAF8YXzBfSF9gX3hfkF+oX8Bf2F/wYAhgIGA4YFBgaGCAYJhgsGDIYOBg+GEQYShhQGFYYXBhiGGgYbhh0GHoYgBiGGIwYkhiYGJ4YpBiqGLAYthi8GMIYyBjOGNQY2hjgGOYY7BjyGPgY/hkEGQoZEBkWGRwZIhkoGS4ZNBk6GUAZRhlMGVIZWBleGWQZahlwGXYZfBmCGYgZjhmUGZoZoBmmGawZshm4Gb4ZxBnKGdAZ1hncGeIZ6BnuGfQZ+hoAGgYaDBoSGhgaHhokGioaMBo2GjwaQhpIGk4aVBpaGmAaZhpsGnIaeBp+GoQaihqQGpYanBqiGqgarhq0GroawBrGGswa0hrYGt4a5BrqGvAa9hr8GwIbCBsOGxQbGhsgGyYbLBsyGzgbPhtEG0obUBtWG1wbYhtoG24bdBt6G4AbhhuMG5IbmBueG6QbqhuwG7YbvBvCG8gbzhvUG9ob4BvmG+wb8hv4G/4cBBwKHBAcFhwcHCIcKBwuHDQcOhxAHEYcTBxSHFgcXhxkHGoccBx2HHwcghyIHI4clByaHKAcphysHLIcuBy+HMQcyhzQHNYc3BziHOgc7hz0HPodAB0GHQwdEh0YHR4dJB0qHTAdNh08HUIdSB1OHVQdWh1gHWYdbB1yHXgdfh2EHYodkB2WHZwdoh2oHa4dtB26HcAdxh3MHdId2B3eHeQd6h3wHfYd/B4CHggeDh4UHhoeIB4mHiweMh44Hj4eRB5KHlAeVh5cHmIeaB5uHnQeeh6AHoYejB6SHpgenh6kHqoesB62Hrwewh7IHs4e1B7aHuAe5h7sHvIe+B7+HwQfCh8QHxYfHB8iHygfLh80HzofQB9GH0wfUh9YH14fZB9qH3Afdh98H4IfiB+OH5Qfmh+gH6YfrB+yH7gfvh/EH8of0B/WH9wf4h/oH+4f9B/6IAAgBiAMIBIgGCAeICQgKiAwIDYgPCBCIEggTiBUIFogYCBmIGwgciB4IH4ghCCKIJAgliCcIKIgqCCuILQguiDAIMYgzCDSINgg3iDkIOog8CD2IPwhAiEIIQ4hFCEaISAhJiEsITIhOCE+IUQhSiFQIVYhXCFiIWghbiF0IXohgCGGIYwhkiGYIZ4hpCGqIbAhtiG8IcIhyCHOIdQh2iHgIeYh7CHyIfgh/iIEIgoiECIWIhwiIiIoIi4iNCI6IkAiRiJMIlIiWCJeImQiaiJwInYifCKCIogijiKUIpoioCKmIqwisiK4Ir4ixCLKItAi1iLcIuIi6CLuIvQi+iMAIwYjDCMSIxgjHiMkIyojMCM2IzwjQiNII04jVCNaI2AjZiNsI3IjeCN+I4QjiiOQI5YjnCOiI6gjriO0I7ojwCPGI8wj0iPYI94j5CPqI/Aj9iP8JAIkCCQOJBQkGiQgJCYkLCQyJDgkPiREJEokUCRWJFwkYiRoJG4kdCR6JIAkhiSMJJIkmCSeJKQkqiSwJLYkvCTCJMgkziTUJNok4CTmJOwk8iT4JP4lBCUKJRAlFiUcJSIlKCUuJTQlOiVAJUYlTCVSJVglXiVkJWolcCV2JXwlgiWIJY4llCWaJaAlpiWsJbIluCW+JcQlyiXQJdYl3CXiJegl7iX0JfomACYGJgwmEiYYJh4mJCYqJjAmNiY8JkImSCZOJlQmWiZgJmYmbCZyJngmfiaEJoomkCaWJpwmoiaoJq4mtCa6JsAmxibMJtIm2CbeJuQm6ibwJvYm/CcCJwgnDicUJxonICcmJywnMic4Jz4nRCdKJ1AnVidcJ2InaCduJ3QneieAJ4YnjCeSJ5gnniekJ6onsCe2J7wnwifIJ84n1CfaJ+An5ifsJ/In+Cf+KAQoCigQKBYoHCgiKCgoLig0KDooQChGKEwoUihYKF4oZChqKHAodih8KIIoiCiOKJQomiigKKYorCiyKLgovijEKMoo0CjWKNwo4ijoKO4o9Cj6KQApBikMKRIpGCkeKSQpKikwKTYpPClCKUgpTilUKVopYClmKWwpcil4KX4phCmKKZAplimcKaIpqCmuKbQpuinAKcYpzCnSKdgp3inkKeop8Cn2KfwqAioIKg4qFCoaKiAqJiosKjIqOCo+KkQqSipQKlYqXCpiKmgqbip0KnoqgCqGKowqkiqYKp4qpCqqKrAqtiq8KsIqyCrOKtQq2irgKuYq7CryKvgq/isEKworECsWKxwrIisoKy4rNCs6K0ArRitMK1IrWCteK2QraitwK3YrfCuCK4grjiuUK5oroCumK6wrsiu4K74rxCvKK9Ar1ivcK+Ir6CvuK/Qr+iwALAYsDCwSLBgsHiwkLCosMCw2LDwsQixILE4sVCxaLGAsZixsLHIseCx+LIQsiiyQLJYsnCyiLKgsriy0LLoswCzGLMws0izYLN4s5CzqLPAs9iz8LQItCC0OLRQtGi0gLSYtLC0yLTgtPi1ELUotUC1WLVwtYi1oLW4tdC16LYAthi2MLZItmC2eLaQtqi2wLbYtvC3CLcgtzi3ULdot4C3mLewt8i34Lf4uBC4KLhAuFi4cLiIuKC4uLjQuOi5ALkYuTC5SLlguXi5kLmoucC52Lnwugi6ILo4ulC6aLqAupi6sLrIuuC6+LsQuyi7QLtYu3C7iLugu7i70LvovAC8GLwwvEi8YLx4vJC8qLzAvNi88L0IvSC9OL1QvWi9gL2YvbC9yL3gvfi+EL4ovkC+WL5wvoi+oL64vtC+6L8Avxi/ML9Iv2C/eL+Qv6i/wL/Yv/DACMAgwDjAUMBowIDAmMCwwMjA4MD4wRDBKMFAwVjBcMGIwaDBuMHQwejCAMIYwjDCSMJgwnjCkMKowsDC2MLwwwjDIMM4w1DDaMOAw5jDsMPIw+DD+MQQxCjEQMRYxHDEiMSgxLjE0MToxQDFGMUwxUjFYMV4xZDFqMXAxdjF8MYIxiDGOMZQxmjGgMaYxrDGyMbgxvjHEMcox0DHWMdwx4jHoMe4x9DH6MgAyBjIMMhIyGDIeMiQyKjIwMjYyPDJCMkgyTjJUMloyYDJmMmwycjJ4Mn4yhDKKMpAyljKcMqIyqDKuMrQyujLAMsYyzDLSMtgy3jLkMuoy8DL2MvwzAjMIMw4zFDMaMyAzJjMsMzIzODM+M0QzSjNQM1YzXDNiM2gzbjN0M3ozgDOGM4wzkjOYM54zpDOqM7AztjO8M8IzyDPOM9Qz2jPgM+Yz7DPyM/gz/jQENAo0EDQWNBw0IjQoNC40NDQ6NEA0RjRMNFI0WDReNGQ0ajRwNHY0fDSCNIg0jjSUNJo0oDSmNKw0sjS4NL40xDTKNNA01jTcNOI06DTuNPQ0+jUANQY1DDUSNRg1HjUkNSo1MDU2NTw1QjVINU41VDVaNWA1ZjVsNXI1eDV+NYQ1ijWQNZY1nDWiNag1rjW0Nbo1wDXGNcw10jXYNd415DXqNfA19jX8NgI2CDYONhQ2GjYgNiY2LDYyNjg2PjZENko2UDZWNlw2YjZoNm42dDZ6NoA2hjaMNpI2mDaeNqQ2qjawNrY2vDbCNsg2zjbUNto24DbmNuw28jb4Nv43BDcKNxA3FjccNyI3KDcuNzQ3OjdAN0Y3TDdSN1g3XjdkN2o3cDd2N3w3gjeI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BZoAAQSgBbgAAQMQAAAAAQMOBZoAAQSgBbgAAQMQAAAAAQMOBZoAAQSgBbgAAQKeAAAAAQKYBZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQAAAAAAAQPlBEgAAQAAAAAAAQJSAAAAAQJtBEgAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQKFAAAAAQKDBEYAAQMbBEgAAQKFAAAAAQKDBEYAAQMbBEgAAQKFAAAAAQKDBEYAAQMbBEgAAQKFAAAAAQKDBEYAAQMbBEgAAQKFAAAAAQKDBEYAAQMbBEgAAQAAAAAAAQKDBEYAAQAAAAAAAQKoAAAAAQKPBEgAAQP4BEgAAQKoAAAAAQKPBEgAAQP4BEgAAQKoAAAAAQKPBEgAAQP4BEgAAQKoAAAAAQKPBEgAAQP4BEgAAQN/AAAAAQIxBEgAAQAAAAAAAQE3AAAAAQE3BfAAAQIvBEgAAQN/AAAAAQIxBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQMMAAAAAQMABZoAAQAAAAAAAQJSAAAAAQJtBEgAAQAAAAAAAQMMAAAAAQMABZoAAQAAAAAAAQJSAAAAAQJtBEgAAQAAAAAAAQMMAAAAAQMABZoAAQAAAAAAAQJSAAAAAQJtBEgAAQAAAAAAAQMMAAAAAQMABZoAAQAAAAAAAQJSAAAAAQJtBEgAAQAAAAAAAQMZAAAAAQLnBZoAAQAAAAAAAQLbAAAAAQAAAAAAAQVcBfAAAQNIAAAAAQMXBZoAAQAAAAAAAQLbAAAAAQAAAAAAAQVcBfAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQMbAAAAAQMfBZoAAQAAAAAAAQAAAAAAAQKRBEgAAQAAAAAAAQMbAAAAAQMfBZoAAQAAAAAAAQAAAAAAAQKRBEgAAQAAAAAAAQMbAAAAAQMfBZoAAQAAAAAAAQAAAAAAAQKRBEgAAQAAAAAAAQMbAAAAAQMfBZoAAQAAAAAAAQAAAAAAAQKRBEgAAQAAAAAAAQMnAAAAAQM1BZoAAQAAAAAAAQLFAAAAAQExBfAAAQInBEwAAQNqAAAAAQN5BZoAAQAAAAAAAQLFAAAAAQExBfAAAQInBEwAAQE5AAAAAQE5BZoAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQE5AAAAAQE5BwYAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQE5AAAAAQE5BZoAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQE5AAAAAQE5BZoAAQAAAAAAAQAAAAAAAQErBEgAAQAAAAAAAQE5AAAAAQRqBZoAAQAAAAAAAQErAAAAAQErBEgAAQAAAAAAAQAAAAAAAQH4BZoAAQAAAAAAAQAAAAAAAQEhBEgAAQAAAAAAAQLXAAAAAQLXBZoAAQAAAAAAAQKWAAAAAQE3BfAAAQIvBEgAAQJgAAAAAQErBEgAAQAAAAAAAQJvAAAAAQE7BZoAAQNCBZoAAQE3AAAAAQE3BfAAAQJQBfAAAQJvAAAAAQE7BZoAAQNCBZoAAQE3AAAAAQE3BfAAAQJQBfAAAQJvAAAAAQE7BZoAAQNCBZoAAQE3AAAAAQE3BfAAAQJQBfAAAQJvAAAAAQE7BZoAAQNCBZoAAQE3AAAAAQE3BfAAAQJQBfAAAQJ1AAAAAQFCBZoAAQNIBZoAAQFeAAAAAQFeBfAAAQJ3BfAAAQMrAAAAAQM7BZoAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQMrAAAAAQM7BZoAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQMrAAAAAQM7BZoAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQOcAAAAAQOwBEgAAQAAAAAAAQMpAAAAAQMpBZoAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQYzAAAAAQRqBZoAAQAAAAAAAQAAAAAAAQQpBEYAAQAAAAAAAQLhAAAAAQLdBZoAAQAAAAAAAQEnAAAAAQHsBEgAAQAAAAAAAQLhAAAAAQLdBZoAAQAAAAAAAQEnAAAAAQHsBEgAAQAAAAAAAQLhAAAAAQLdBZoAAQAAAAAAAQEnAAAAAQHsBEgAAQAAAAAAAQKPAAAAAQKaBZoAAQAAAAAAAQHsAAAAAQH8BEgAAQAAAAAAAQKPAAAAAQKaBZoAAQAAAAAAAQHsAAAAAQH8BEgAAQAAAAAAAQKPAAAAAQKaBZoAAQAAAAAAAQHsAAAAAQH8BEgAAQAAAAAAAQKPAAAAAQKaBZoAAQAAAAAAAQHsAAAAAQH8BEgAAQAAAAAAAQJYAAAAAQJtBZoAAQAAAAAAAQH+AAAAAQFYBUgAAQJYBfAAAQJYAAAAAQJtBZoAAQAAAAAAAQH+AAAAAQFYBUgAAQJYBfAAAQH+AAAAAQAAAAAAAQAAAAAAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKPBEgAAQP4BEgAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKPBEgAAQP4BEgAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKPBEgAAQP4BEgAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKPBEgAAQP4BEgAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKPBEgAAQP4BEgAAQQxAAAAAQQxBZoAAQAAAAAAAQAAAAAAAQOmBEgAAQAAAAAAAQKeAAAAAQKYBZoAAQAAAAAAAQN/AAAAAQIxBEgAAQAAAAAAAQKeAAAAAQKYBZoAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQIxAAAAAQIzBEgAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQIxAAAAAQIzBEgAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQIxAAAAAQIzBEgAAQAAAAAAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKYBEgAAQP4BEgAAQjbAAAAAQjjBZoAAQAAAAAAAQhgAAAAAQhiBEgAAQAAAAAAAQeoAAAAAQeqBEgAAQVcBfAAAQJvAAAAAQZMBZoAAQNCBZoAAQJvAAAAAQE7BZoAAQNCBZoAAQE3AAAAAQE3BfAAAQJQBfAAAQMrAAAAAQhKBZoAAQAAAAAAAQMrAAAAAQM7BZoAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQMbAAAAAQMfBx8AAQAAAAAAAQAAAAAAAQKRBc0AAQAAAAAAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQL0AAAAAQL+CQIAAQAAAAAAAQJIAAAAAQJYB7AAAQAAAAAAAQXpAAAAAQUxByEAAQAAAAAAAQAAAAAAAQPlBc8AAQAAAAAAAQM/AAAAAQM7ByEAAQPFBbgAAQAAAAAAAQKDBc0AAQAAAAAAAQL0AAAAAQL+ByEAAQAAAAAAAQJIAAAAAQJYBc8AAQAAAAAAAQL0AAAAAQL+ByEAAQAAAAAAAQJIAAAAAQJYBc8AAQAAAAAAAQLJAAAAAQLPByEAAQAAAAAAAQJoAAAAAQJkBc0AAQAAAAAAAQLJAAAAAQLPByEAAQAAAAAAAQJoAAAAAQJkBc0AAQAAAAAAAQE5AAAAAQE5ByEAAQAAAAAAAQAAAAAAAQErBc8AAQAAAAAAAQE5AAAAAQE5ByEAAQAAAAAAAQAAAAAAAQErBc8AAQAAAAAAAQM/AAAAAQMxByEAAQPFBbgAAQKFAAAAAQKDBc0AAQMbBEgAAQM/AAAAAQMxByEAAQPFBbgAAQKFAAAAAQKDBc0AAQMbBEgAAQLhAAAAAQLdByEAAQAAAAAAAQEnAAAAAQHsBc8AAQAAAAAAAQLhAAAAAQLdByEAAQAAAAAAAQEnAAAAAQHsBc8AAQAAAAAAAQMQAAAAAQMOByEAAQSgBbgAAQKoAAAAAQKPBc8AAQP4BEgAAQMQAAAAAQMOByEAAQSgBbgAAQKoAAAAAQKPBc8AAQP4BEgAAQKPAAAAAQKaBZoAAQAAAAAAAQHsAAAAAQH8BEgAAQAAAAAAAQJYAAAAAQJtBZoAAQAAAAAAAQH+AAAAAQFYBUgAAQJYBfAAAQM/AAAAAQMxCKIAAQPFBbgAAQKFAAAAAQKDB04AAQMbBEgAAQM/AAAAAQMxCKIAAQPFBbgAAQKFAAAAAQKDB04AAQMbBEgAAQM/AAAAAQMxCIkAAQPFBbgAAQKFAAAAAQKDBzUAAQMbBEgAAQKeAAAAAQKYBwYAAQAAAAAAAQN/AAAAAQIxBbQAAQAAAAAAAQAAAAAAAQEhBEgAAQAAAAAAAQJYAAIAAQJUBEgAAQAAAAAAAQMMAAAAAQMAByEAAQAAAAAAAQJSAAAAAQJtBc8AAQAAAAAAAQMZAAAAAQLnBZoAAQAAAAAAAQLbAAAAAQAAAAAAAQVcBfAAAQMZAAAAAQLnBZoAAQAAAAAAAQLbAAAAAQAAAAAAAQVcBfAAAQLJAAAAAQLPCI0AAQAAAAAAAQJoAAAAAQJkBzkAAQAAAAAAAQLJAAAAAQLPCI0AAQAAAAAAAQJoAAAAAQJkBzkAAQAAAAAAAQLJAAAAAQLPByEAAQAAAAAAAQJoAAAAAQJkBc0AAQAAAAAAAQMbAAAAAQMfBwYAAQAAAAAAAQAAAAAAAQKRBbQAAQAAAAAAAQMnAAAAAQM1BZoAAQAAAAAAAQLFAAAAAQExBfAAAQInBEwAAQMnAAAAAQM1BZoAAQAAAAAAAQLFAAAAAQExBfAAAQInBEwAAQE5AAAAAQE5CLwAAQAAAAAAAQAAAAAAAQErB2oAAQAAAAAAAQJvAAAAAQE7BZoAAQNCBZoAAQE3AAAAAQE3BfAAAQJQBfAAAQJvAAAAAQE7BZoAAQNCBZoAAQE3AAAAAQE3BfAAAQJQBfAAAQOwAAAAAQAAAAAAAQAAAAAAAQQ7AAAAAQAAAAAAAQAAAAAAAQMrAAAAAQM7Bx0AAQAAAAAAAQK0AAAAAQLJBcsAAQAAAAAAAQMrAAAAAQM7BZoAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQMrAAAAAQM7BZoAAQAAAAAAAQK0AAAAAQLJBEgAAQAAAAAAAQM/AAAAAQMxCLwAAQPFBbgAAQKFAAAAAQKDB2gAAQMbBEgAAQM/AAAAAQMxCNEAAQPFBbgAAQKFAAAAAQKDB30AAQMbBEgAAQM/AAAAAQMxCI0AAQPFBbgAAQKFAAAAAQKDBzkAAQMbBEgAAQM/AAAAAQMxCI0AAQPFBbgAAQKFAAAAAQKDBzkAAQMbBEgAAQLhAAAAAQLdBZoAAQAAAAAAAQEnAAAAAQHsBEgAAQAAAAAAAQLhAAAAAQLdBZoAAQAAAAAAAQEnAAAAAQHsBEgAAQAAAAAAAQKPAAAAAQKaBx0AAQAAAAAAAQHsAAAAAQH8BcsAAQAAAAAAAQKPAAAAAQKaBZoAAQAAAAAAAQHsAAAAAQH8BEgAAQAAAAAAAQKPAAAAAQKaCKQAAQAAAAAAAQHsAAAAAQH8B1IAAQAAAAAAAQKPAAAAAQKaCKIAAQAAAAAAAQHsAAAAAQH8B1AAAQAAAAAAAQKPAAAAAQKaBx0AAQAAAAAAAQHsAAAAAQH8BcsAAQAAAAAAAQJYAAAAAQJtBZoAAQAAAAAAAQH+AAAAAQFYBUgAAQJYBfAAAQJYAAAAAQJtBZoAAQAAAAAAAQH+AAAAAQFYBUgAAQJYBfAAAQMQAAAAAQMOCLwAAQSgBbgAAQKoAAAAAQKPB2oAAQP4BEgAAQMQAAAAAQMOCKIAAQSgBbgAAQKoAAAAAQKPB1AAAQP4BEgAAQQxAAAAAQQxBZoAAQAAAAAAAQAAAAAAAQOmBEgAAQAAAAAAAQQxAAAAAQQxBZoAAQAAAAAAAQAAAAAAAQOmBEgAAQAAAAAAAQQxAAAAAQQxBZoAAQAAAAAAAQAAAAAAAQOmBEgAAQAAAAAAAQKeAAAAAQKYBx0AAQAAAAAAAQN/AAAAAQIxBcsAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQIxAAAAAQIzBEgAAQAAAAAAAQH+AAAAAQFYBuMAAQJYBfAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL6B1oAAQAAAAAAAQJIAAAAAQJUBggAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQL0AAAAAQL+BZoAAQAAAAAAAQJIAAAAAQJYBEgAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLLB1oAAQAAAAAAAQJoAAAAAQJgBgYAAQAAAAAAAQLJAAAAAQLPBzUAAQAAAAAAAQJoAAAAAQJkBeEAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQLJAAAAAQLPBZoAAQAAAAAAAQJoAAAAAQJkBEYAAQAAAAAAAQE5AAAAAQE1B1oAAQAAAAAAAQAAAAAAAQEnBggAAQAAAAAAAQE5AAAAAQE5BZoAAQAAAAAAAQErAAAAAQErBEgAAQAAAAAAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMtB1oAAQPFBbgAAQKFAAAAAQJ/BgYAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQM/AAAAAQMxByEAAQPFBbgAAQKFAAAAAQKDBc0AAQMbBEgAAQM/AAAAAQMxByEAAQPFBbgAAQKFAAAAAQKDBc0AAQMbBEgAAQM/AAAAAQMtB1oAAQPFBbgAAQKFAAAAAQJ/BgYAAQMbBEgAAQM/AAAAAQMxBzUAAQPFBbgAAQKFAAAAAQKDBeEAAQMbBEgAAQM/AAAAAQMxBZoAAQPFBbgAAQKFAAAAAQKDBEYAAQMbBEgAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKPBEgAAQP4BEgAAQMQAAAAAQMKB1oAAQSgBbgAAQKoAAAAAQKLBggAAQP4BEgAAQMQAAAAAQMOByEAAQSgBbgAAQKoAAAAAQKYBc8AAQP4BEgAAQMQAAAAAQMOByEAAQSgBbgAAQKoAAAAAQKYBc8AAQP4BEgAAQMQAAAAAQMKB1oAAQSgBbgAAQKoAAAAAQKTBggAAQP4BEgAAQMQAAAAAQMOBzUAAQSgBbgAAQKoAAAAAQKYBeMAAQP4BEgAAQMQAAAAAQMOBZoAAQSgBbgAAQKoAAAAAQKYBEgAAQP4BEgAAQKeAAAAAQKYBZoAAQAAAAAAAQN/AAAAAQIxBEgAAQAAAAAAAQKeAAAAAQKYBZoAAQAAAAAAAQN/AAAAAQIxBEgAAQAAAAAAAQKeAAAAAQKTB1oAAQAAAAAAAQN/AAAAAQItBggAAQAAAAAAAQKeAAAAAQKYBzUAAQAAAAAAAQN/AAAAAQIxBeMAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQByEAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMMB1oAAQAAAAAAAQMQAAAAAQMQByEAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQMQAAAAAQMQCQIAAQAAAAAAAQMQAAAAAQMQBZoAAQAAAAAAAQjbAAAAAQjjBZoAAQAAAAAAAQhgAAAAAQhqBEgAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgByEAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgByEAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKcB1oAAQAAAAAAAQKRAAAAAQKgByEAAQAAAAAAAQKRAAAAAQKgBZoAAQAAAAAAAQKRAAAAAQKgCI0AAQAAAAAAAQKRAAAAAQKgCI0AAQAAAAAAAQKRAAAAAQKgBzUAAQAAAAAAAQMKAAAAAQMKBZoAAQAAAAAAAQMKAAAAAQMKBZoAAQAAAAAAAQMKAAAAAQMKBx8AAQAAAAAAAQMKAAAAAQMKBZoAAQAAAAAAAQMKAAAAAQMKBZoAAQAAAAAAAQMKAAAAAQMKBZoAAQAAAAAAAQMKAAAAAQMKBwYAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQX+BZoAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQE5AAAAAQCFBwoAAQAAAAAAAQH2AAAAAQX+ByEAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQH2ByEAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQH2CLwAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQHyB1oAAQAAAAAAAQH2AAAAAQH2ByEAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQH2AAAAAQH2BZoAAQAAAAAAAQAAAAAAAQISBZoAAQAAAAAAAQAAAAAAAQISByEAAQAAAAAAAQJvAAAAAQZmBZoAAQNCBZoAAQTPAAAAAQE5BZoAAQAAAAAAAQTPAAAAAQE5BZoAAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQMfAAAAAQg3BZoAAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQMfAAAAAQNtBx0AAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQMSAAAAAQMZBZoAAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQMfAAAAAQNtBZoAAQAAAAAAAQM/AAAAAQMxBZoAAQPFBbgAAQKgAAAAAQKmBZoAAQAAAAAAAQKgAAAAAQKmBZoAAQAAAAAAAQKgAAAAAQKmBZoAAQAAAAAAAQKgAAAAAQKmBZoAAQAAAAAAAQKgAAAAAQKmBZoAAQAAAAAAAQKgAAAAAQKmBZoAAQAAAAAAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTByEAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLPB1oAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTByEAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTByEAAQRvBbgAAQLsAAAAAQLPB1oAAQRvBbgAAQLsAAAAAQLTBzUAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTByEAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTCKIAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTBZoAAQRvBbgAAQLsAAAAAQLTCLwAAQRvBbgAAQAAAAAAAQS0BZoAAQAAAAAAAQAAAAAAAQS0BZoAAQAAAAAAAQAAAAAAAQS0BZoAAQAAAAAAAQAAAAAAAQS0BZoAAQAAAAAAAQAAAAAAAQS0BZoAAQAAAAAAAQMG/z0AAQL2BZoAAQAAAAAAAQMG/z0AAQL2BZoAAQAAAAAAAQMG/z0AAQL2BZoAAQAAAAAAAQMG/z0AAQL2BZoAAQAAAAAAAQMG/z0AAQL2Bx0AAQAAAAAAAQMG/z0AAQL2BZoAAQAAAAAAAQMG/z0AAQL2BZoAAQAAAAAAAQMG/z0AAQLyB1oAAQAAAAAAAQMG/z0AAQL2BwYAAQAAAAAAAQMG/z0AAQL2BzUAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQKsAAAAAQK0BZoAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBc8AAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKsBggAAQAAAAAAAQKqAAAAAQKwBc8AAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQKqAAAAAQKwB7AAAQAAAAAAAQKqAAAAAQKwBEgAAQAAAAAAAQAAAAAAAQPRBEgAAQAAAAAAAQAAAAAAAQPRBc8AAQAAAAAAAQeoAAAAAQeyBEgAAQVcBfAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBc8AAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBc8AAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJoBggAAQAAAAAAAQJvAAAAAQJtBc8AAQAAAAAAAQJvAAAAAQJtBEgAAQAAAAAAAQJvAAAAAQJtBzsAAQAAAAAAAQJvAAAAAQJtBzsAAQAAAAAAAQJvAAAAAQJtBeMAAQAAAAAAAQI/AAAAAQI9BEgAAQAAAAAAAQAAAAAAAQNtBc8AAQAAAAAAAQAAAAAAAQErBcsAAQAAAAAAAQFKAAAAAQEZBfAAAQIxBfAAAQFKAAAAAQEZBfAAAQIxBfAAAQFKAAAAAQEZBfAAAQIxBfAAAQFKAAAAAQEZBfAAAQIxBfAAAQFKAAAAAQEZBfAAAQIxBfAAAQFKAAAAAQEZBfAAAQIxBfAAAQFKAAAAAQEZBfAAAQIxBfAAAQFKAAAAAQEZBfAAAQIxBfAAAQGFAAAAAQFUBfAAAQJtBfAAAQdzAAAAAQdzBEgAAQAAAAAAAQHLAAAAAQEQBUgAAQIQBfAAAQGeAAAAAQAAAAAAAQAAAAAAAQHLAAAAAQEQBUgAAQIQBfAAAQHLAAAAAQEQBUgAAQIQBfAAAQHLAAAAAQEQBUgAAQIQBfAAAQHLAAAAAQEQBuMAAQIQBfAAAQHLAAAAAQEQBUgAAQIQBfAAAQHLAAAAAQEQBUgAAQIQBfAAAQAAAAAAAQPlBEgAAQAAAAAAAQAAAAAAAQPlBEgAAQAAAAAAAQAAAAAAAQPlBEgAAQAAAAAAAQAAAAAAAQPlBEgAAQAAAAAAAQAAAAAAAQPlBEgAAQAAAAAAAQVMAAAAAQKYBEgAAQAAAAAAAQVMAAAAAQKYBEgAAQAAAAAAAQVMAAAAAQKYBEgAAQAAAAAAAQVMAAAAAQKYBEgAAQAAAAAAAQVMAAAAAQKYBEgAAQAAAAAAAQVMAAAAAQKYBEgAAQAAAAAAAQVMAAAAAQKTBggAAQAAAAAAAQVMAAAAAQKYBbQAAQAAAAAAAQVMAAAAAQKYBeMAAQAAAAAAAQIxAAAAAQI7BEgAAQAAAAAAAQIxAAAAAQI7BEgAAQAAAAAAAQIxAAAAAQI7BEgAAQAAAAAAAQIxAAAAAQI7BEgAAQAAAAAAAQIxAAAAAQI7BEgAAQAAAAAAAgAiACYAJgAAACgAKgABACwANAAEADYAOgANADwAPAASAD4APwATAEYARgAVAEgASgAWAEwATgAZAFAAVAAcAFcAWgAhAFwAXAAlAF4AXwAmAIQAmgAoAJwAoQA/AKQAswBFALUAugBVALwAxwBbAMoA2wBnAN4A8QB5APQBKQCNASsBNQDDATgBQgDOAUUBgQDZAa8B9wEWAfkCUgFfAqkCvQG5Ar8C2AHOAtoC2gHoAtwDBQHpAwcDBwITAwkDaQIUA2sDbAJ1A24DlAJ3AAIAAgGTAakAAAPSA+AAFwAmAAEAmgABAKAAAQCmAAEArAABALIAAQC4AAEAvgABAMQAAQDKAAEA0AABANYAAQDcAAEA4gABAOgAAQDuAAIA9AAAAPoAAAEAAAABBgAAAQwAAAESAAABGAAAAR4AAgEkAAEBKgABATAAAQE2AAEBPAAAAUIAAgFIAAEBTgABAVQAAQFaAAEBYAABAWYAAQFsAAEBcgABAXgAAQApBEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAf/+BEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAABZoAAQAABEgAAf/+BEgAAQAABEgAAQAABEgAAQAAAAAAAQAABZoAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQAABEgAAQCIAH4AAQCeAAwADgAeACQAKgAwADYAPABCAEgATgBUAFoAYABmAGwAAQApBc8AAQAABc8AAQAABc8AAQAABeMAAQAABbQAAQAABc8AAQAABcsAAQAABeMAAf/6BggAAQAABikAAQAABc8AAQAABc0AAQAABc8AAQAABc8AAgABAZMBoAAAAAIAAwGTAaEAAAPTA9YADwPZA+AAEwAbAAAAbgAAAHQAAAB6AAAAgAAAAIYAAACMAAAAkgAAAJgAAACeAAAApAAAAKoAAACwAAAAtgAAALwAAADCAAAAyAAAAM4AAADUAAAA2gAAAOAAAADmAAAA7AAAAPIAAAD4AAAA/gAAAQQAAAEKAAEAKQRIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAH//gRIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAH//gRIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAEAAARIAAAAAQAAAADVpCcIAAAAANRTKbUAAAAA3hPgAw==') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_lib.js
================================================
/**
* Notes: 日历组件通用方法
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux@qq.com
* Date: 2021-01-01 07:48:00
*/
const lunarLib = require('../../../lib/tools/lunar_lib.js');
const timeHelper = require('../../../helper/time_helper.js');
const dataHelper = require('../../../helper/data_helper.js');
const pageHelper = require('../../../helper/page_helper.js');
// 是否节日
function isHoliday(day) {
if (!day) return false;
let arr = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '初', '廿'];
for (let k = 0; k < arr.length; k++) {
if (day.includes(arr[k]))
return false;
}
return true;
}
// 某天是某月的第几周
function weekIndexInMonth(year = null, month = null, today = null) {
let time = new Date();
if (year == null) { // 没有设定,则取当前
year = time.getFullYear();
month = time.getMonth();
today = time.getDate();
} else {
year = Number(year);
month = Number(month) - 1;
today = Number(today);
}
time = new Date(year, month, 1); //取第一天
let space = time.getDay() - 1; //获取当前星期X(0-6,0代表星期天)
if (space == -1) space = 6; //调整为周1-周7格式
return Math.ceil((today + space) / 7); // 本月第几周
}
// 日期格式化,一位补2位
function fmtDate(str) {
str = str + '';
if (str.length == 1)
return '0' + str;
else
return str;
}
// 获取当前时间,若定义了要操作的天, 则取得年份和月份,用以构造日历
function getNowTime(that) {
const time = new Date();
let year = time.getFullYear();
let month = time.getMonth() + 1;
let week = time.getDay();
let today = time.getDate(); // 今天
let fullToday = year + '-' + fmtDate(month) + '-' + fmtDate(today); // 今天完整格式
// 若定义了要操作的天, 则取得年份和月份,用以构造日历
if (that.data.mode == 'one' && that.data.oneDoDay) {
year = Number(timeHelper.timestamp2Time(timeHelper.time2Timestamp(that.data.oneDoDay), 'Y'));
month = Number(timeHelper.timestamp2Time(timeHelper.time2Timestamp(that.data.oneDoDay), 'M'));
} else if (that.data.mode == 'multi' && that.data.multiDoDay && that.data.multiDoDay.length > 0 && that.data.multiDoDay[0]) {
year = Number(timeHelper.timestamp2Time(timeHelper.time2Timestamp(that.data.multiDoDay[0]), 'Y'));
month = Number(timeHelper.timestamp2Time(timeHelper.time2Timestamp(that.data.multiDoDay[0]), 'M'));
}
let oneDoDay = that.data.oneDoDay || fullToday; // 正在操作的天完整格式
// let multiDoDay = that.data.multiDoDay || [oneDoDay]; // 多选默认选中一天
let multiDoDay = that.data.multiDoDay;
that.setData({
year,
month,
fullToday,
oneDoDay,
multiDoDay,
week
});
}
//获得某月天数
function getMonthCnt(year, month) {
let baseMonthsDay = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; //各月天数
if (month == 2 && year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
return 29;
else
return baseMonthsDay[month - 1];
}
//获得上月天数
function getLastMonthCnt(year, month) {
if (month == 1) {
month = 12;
--year;
} else
--month;
return getMonthCnt(year, month);
}
//从上月补几天
function getLastMonthArr(that, year, month) {
let time = new Date(year, month - 1, 1);
let space = time.getDay() - 1; //获取当前星期X(0-6,0代表星期天)
if (space == -1) space = 6; //调整为周1-周7格式
// 获取上个月天数
let lastMonthCnt = getLastMonthCnt(year, month);
if (month == 1) {
month = 12;
--year;
} else
--month;
let dayArr = [];
for (let i = space; i >= 1; i--) {
let lunar = that.data.isLunar ? lunarLib.sloarToLunar(year, month, lastMonthCnt - i + 1) : '';
let holiday = isHoliday(lunar);
let full = year + '-' + fmtDate(month) + '-' + fmtDate(lastMonthCnt - i + 1);
dayArr.push({
lunar,
holiday,
show: lastMonthCnt - i + 1,
curMonth: false,
weekNo: 1, //必然第一周
val: year + '-' + month + '-' + (lastMonthCnt - i + 1),
full
});
}
return dayArr;
}
/**
* //从下月补几天
* @param {*} year
* @param {*} month
* @param {*} hasDayLen 已有多少天
*/
function getNextMonthArr(that, year, month, hasDayLen) {
if (that.mode == 'multi') {
if (month == 12) {
month = 1;
++year;
} else
month++;
// 多选统一补成6行
let dayArr = [];
for (let i = 1; i <= (6 * 7 - hasDayLen); i++) {
let weekNo = Math.ceil((hasDayLen + i) / 7); // 计算当前是第几周
let lunar = that.data.isLunar ? lunarLib.sloarToLunar(year, month, i) : '';
let holiday = isHoliday(lunar);
let full = year + '-' + fmtDate(month) + '-' + fmtDate(i);
dayArr.push({
lunar,
holiday,
has: false, //是否有数据
show: i,
curMonth: false,
weekNo,
val: year + '-' + month + '-' + i,
full
});
}
} else {
// 单选只把最后一行补齐
let endDay = getMonthCnt(year, month); //最后一天
let time = new Date(year, month - 1, endDay);
let space = time.getDay(); //获取当前星期X(0-6,0代表星期天)
space = 7 - space;
if (space == 7) space = 0;
if (space <= 0) return [];
if (month == 12) {
month = 1;
++year;
} else
month++;
let dayArr = [];
for (let i = 1; i <= space; i++) {
let lunar = that.data.isLunar ? lunarLib.sloarToLunar(year, month, i) : '';
let holiday = isHoliday(lunar);
let full = year + '-' + fmtDate(month) + '-' + fmtDate(i);
dayArr.push({
lunar,
holiday,
show: i,
curMonth: false,
weekNo: that.data.weekNo,
val: year + '-' + month + '-' + i,
full
});
}
return dayArr;
}
return dayArr;
}
function createDay(that) {
// 创建日历
let month = that.data.month;
let year = that.data.year;
let dayArr = [];
let len = getMonthCnt(year, month);
for (let i = 1; i <= len; i++) {
let lunar = that.data.isLunar ? lunarLib.sloarToLunar(year, month, i) : '';
let holiday = isHoliday(lunar);
let full = year + '-' + fmtDate(month) + '-' + fmtDate(i) //实际日期(补位);
dayArr.push({
lunar,
holiday,
has: false, //是否有数据
show: i, // 显示
curMonth: true, //是否当前月
weekNo: weekIndexInMonth(year, month, i), //第几周
val: year + '-' + month + '-' + i, //实际日期(简化)
full
});
}
// 前后补空
let lastArr = getLastMonthArr(that, year, month);
let nextArr = getNextMonthArr(that, year, month, dayArr.length + lastArr.length);
let data = lastArr.concat(dayArr).concat(nextArr);
/*
// 数据循环处理
let hasDays = that.data.hasDays;
if (hasDays.length > 0) {
for (let j in hasDays) {
for (let k = 0; k < data.length; k++) {
if (data[k].full == hasDays[j]) {
data[k].has = true; //当日有数据
}
}
}
}*/
// 当前操作日为周几? 仅针对单选模式
let weekNo = 0;
if (that.data.mode == 'one') {
if (!that.data.oneDoDay) {
weekNo = weekIndexInMonth();
} else {
let arr = that.data.oneDoDay.split('-');
weekNo = weekIndexInMonth(arr[0], arr[1], arr[2]);
}
}
that.setData({
weekNo,
dayArr: data
});
}
/** ListTouch计算滚动 */
function listTouchEnd(that) {
if (that.data.touchDirection == 'left') {
that.setData({
animation: 'slide-left'
});
setTimeout(function () {
that.setData({
animation: ''
})
}, 200);
that.bindNextTap();
} else if (that.data.touchDirection == 'right') {
that.setData({
animation: 'slide-right'
});
setTimeout(function () {
that.setData({
animation: ''
})
}, 200);
that.bindLastTap();
}
that.setData({
touchDirection: null
});
}
// 回本月
function bindToNowTap(that) {
const time = new Date();
let year = time.getFullYear();
let month = time.getMonth() + 1;
that.setData({
month,
year,
fold: false
});
that.setData({
animation: 'fade'
});
setTimeout(function () {
that.setData({
animation: ''
})
}, 300);
createDay(that);
if (that.data.mode == 'one') {
//月份切换引起父组件变动
that.triggerEvent('monthChange', {
yearMonth: that.data.year + '-' + dataHelper.padLeft(that.data.month, 2, '0')
});
}
}
//多选天点击
function bindDayMultiTap(e, that) {
// 显示
let oneDoDay = e.currentTarget.dataset.fullday;
let multiDoDay = dataHelper.deepClone(that.data.multiDoDay);
if (that.data.multiOnlyOne) {
// 只能选一个
multiDoDay = [oneDoDay];
} else {
multiDoDay = dataHelper.arrAddDel(multiDoDay, oneDoDay);
}
if (multiDoDay.length < that.data.multiDoDay.length) {
// 有取消
that.triggerEvent('cancel', {
day: oneDoDay
});
}
that.setData({
multiDoDay
});
// 传递给父组件
that.triggerEvent('click', {
days: multiDoDay
});
}
//单个天点击
function bindDayOneTap(e, that) {
// 显示
let oneDoDay = e.currentTarget.dataset.fullday;
// 当前周
let weekNo = 0;
let arr = oneDoDay.split('-');
weekNo = weekIndexInMonth(arr[0], arr[1], arr[2]);
that.setData({
oneDoDay,
weekNo
});
// 传递给父组件
that.triggerEvent('click', {
day: oneDoDay
});
}
// 下月
function bindNextTap(that) {
let month = that.data.month;
if (month == 12) {
that.setData({
year: that.data.year + 1,
month: 1,
fold: false //翻页不折叠
})
} else {
that.setData({
month: month + 1,
fold: false
})
}
createDay(that);
if (that.data.mode == 'one') {
//月份切换引起父组件变动
that.triggerEvent('monthChange', {
yearMonth: that.data.year + '-' + dataHelper.padLeft(that.data.month, 2, '0')
});
}
}
// 上个月
function bindLastTap(that) {
let month = that.data.month;
if (month == 1) {
that.setData({
year: that.data.year - 1,
month: 12,
fold: false
})
} else {
that.setData({
month: month - 1,
fold: false
})
}
createDay(that);
if (that.data.mode == 'one') {
//月份切换引起父组件变动
that.triggerEvent('monthChange', {
yearMonth: that.data.year + '-' + dataHelper.padLeft(that.data.month, 2, '0')
});
}
}
// 日历折叠
function bindFoldTap(that) {
if (that.data.fold)
that.setData({
fold: false
});
else {
//that._init(); //折叠回本月
that.setData({
fold: true
});
}
}
module.exports = {
isHoliday,
weekIndexInMonth,
fmtDate,
getNowTime,
getMonthCnt,
getLastMonthCnt,
getLastMonthArr,
getNextMonthArr,
createDay,
listTouchEnd,
bindToNowTap,
bindDayMultiTap,
bindDayOneTap,
bindNextTap,
bindLastTap,
bindFoldTap
}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_meet/calendar_meet_cmpt.js
================================================
const timeHelper = require('../../../../helper/time_helper.js');
const pageHelper = require('../../../../helper/page_helper.js');
const calendarLib = require('../calendar_lib.js');
/*#### 父组件日历颜色定义*/
/* 整体颜色 */
//--calendarPageColor: #F0F4FF;
/* 加重颜色*/
//--calendarMainColor: #388AFF;
/* 加重的亮颜色 用于选中日期的数据小圆点 */
//--calendarLightColor: #A2C7FF;
Component({
options: {
addGlobalClass: true
},
properties: {
isLunar: { //是否开启农历
type: Boolean,
value: false
},
mode: { // 模式 one/multi
type: String,
value: 'one'
},
year: { // 正在操作的年
type: Number,
value: 0
},
month: { // 正在操作的月
type: Number,
value: 0
},
fold: { //日历折叠
type: Boolean,
value: false
},
selectTimeout: { //过期时间选择(mode=multi)
type: Boolean,
value: true
},
hasDays: { // 过期有数据的日期
type: Array,
value: [],
observer: function (newVal, oldVal) {
if (newVal.length != oldVal.length) {
// TODO 引起加载的时候二次调用
//this._init();
}
}
},
hasJoinDays: { // 未超期有预约的日期
type: Array,
value: [],
},
oneDoDay: { // 正在操作的天 string
type: String,
value: null
},
multiDoDay: { // 多选模式>正在操作的天 arrary[]
type: Array,
value: null,
},
multiOnlyOne: { //多选模式>只能选一个
type: Boolean,
value: false
}
},
data: {
weekNo: 0, // 正在操作的那天位于第几周
fullToday: 0, //今天
glow: '', //闪烁效果
},
lifetimes: {
attached() {
this._init();
}
},
methods: {
_init: function () {
calendarLib.getNowTime(this);
calendarLib.createDay(this);
},
bindFoldTap: function (e) { // 日历折叠
calendarLib.bindFoldTap(this);
},
bindNextTap(e) { // 下月
calendarLib.bindNextTap(this);
this.setData({
glow: 'glow'
});
setTimeout(() => {
this.setData({
glow: ''
});
}, 800);
},
bindLastTap(e) { // 上月
calendarLib.bindLastTap(this);
this.setData({
glow: 'glow'
});
setTimeout(() => {
this.setData({
glow: ''
});
}, 300);
},
bindDayOneTap(e) { // 单个天点击
calendarLib.bindDayOneTap(e, this);
},
bindDayMultiTap(e) { // 多选天点击
let day = e.currentTarget.dataset.fullday;
// 过期时间判断
if (!this.data.selectTimeout) {
let now = timeHelper.time('Y-M-D');
if (day < now)
return pageHelper.showNoneToast('不能编辑过往的日期');
}
// 是否有预约判断
if (this.data.hasJoinDays.includes(day)) {
return pageHelper.showModal('该日期已有用户预约/预约待审核,不可直接取消;如果确要取消,请先删除有预约的时段');
}
calendarLib.bindDayMultiTap(e, this);
},
bindToNowTap: function (e) { // 回本月
calendarLib.bindToNowTap(this);
},
// ListTouch触摸开始
listTouchStart(e) {
pageHelper.listTouchStart(e, this);
},
// ListTouch计算方向
listTouchMove(e) {
pageHelper.listTouchMove(e, this);
},
/** ListTouch计算滚动 */
listTouchEnd: function (e) {
calendarLib.listTouchEnd(this);
}
}
})
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_meet/calendar_meet_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_meet/calendar_meet_cmpt.wxml
================================================
// 比较操作日期所在月是否当前显示的月
function compareYearMonth(oneDoDay, year, month) {
var arr = oneDoDay.split('-');
return arr[0] == year && arr[1] == month;
}
module.exports = {
compareYearMonth: compareYearMonth,
};
本月
{{year}}年{{month}}月
一
二
三
四
五
六
日
{{item.show}}
{{item.lunar}}
================================================
FILE: miniprogram/cmpts/public/calendar/calendar_meet/calendar_meet_cmpt.wxss
================================================
page {
/*#### 父组件日历颜色定义*/
/* 整体颜色 */
--calendarPageColor: #F0F4FF;
/* 加重颜色*/
--calendarMainColor: #1F6ED4;
/* 加重的亮颜色 用于选中日期的数据小圆点 */
--calendarLightColor: #A2C7FF;
}
.calendar-text {
color: var(--calendarMainColor) !important
}
.calendar-bg {
background-color: var(--calendarMainColor) !important
}
.cal-container {
width: 100%;
padding-top: 10rpx;
padding-bottom: 20rpx;
background-color: #fff;
display: flex;
align-items: center;
}
.cal-container .left,
.cal-container .right {
width: 70rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 80rpx;
height: 400rpx;
color: #888;
}
.cal-container .cal-nav {
position: relative;
width: 100%;
min-height: 60rpx;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
color: #000;
border-bottom-left-radius: 15rpx;
border-bottom-right-radius: 15rpx;
font-size: 32rpx;
font-weight: bold;
}
.cal-container .cal-nav .select-item {
width: 500rpx;
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.cal-container .cal-nav .arrow {
width: 150rpx;
font-size: 40rpx;
}
.cal-container .cal-nav .fold {
position: absolute;
right: 0rpx;
width: 100rpx;
font-size: 40rpx;
font-weight: bold;
}
.cal-container .cal-nav .to-now {
position: absolute;
left: 5rpx;
width: 100rpx;
font-size: 30rpx;
color: var(--calendarMainColor);
display: flex;
align-items: center;
justify-content: center;
}
.cal-main {
flex: 1;
padding: 10rpx 0rpx 20rpx;
background-color: #fcfcfc;
}
.cal-title {
display: flex;
justify-content: center;
}
.cal-title view {
width: 80rpx;
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
color: #666;
}
.cal-center {
display: flex;
flex-direction: row;
flex-wrap: wrap;
overflow: hidden;
justify-content: center;
align-items: center;
}
.cal-center.cur {
border-bottom-right-radius: 0rpx;
border-bottom-left-radius: 0rpx;
}
.cal-center .cube {
width: 80rpx;
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
color: #333;
border: 1rpx solid #ccc;
}
.cal-center .cube.glow {
animation: glow 300ms linear 1 alternate;
}
@keyframes glow {
0% {
background-color: #ececec;
}
100% {
background-color: inherit;
}
}
.cal-center .cube.lunar {
margin-bottom: 8rpx;
}
.cal-center .num-grid {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.cal-center .cube.lunar .num-grid {
width: 85rpx;
height: 85rpx;
}
.cal-center .num-grid.now-day-cur {
background-color: orange;
height: 60rpx;
width: 60rpx;
border-radius: 50%;
color: #fff;
}
.cal-center .num-grid.now-day-cur .text-no-month {
color: #fff;
}
.cal-center .num {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 30rpx;
}
.cal-center .num .lunar {
font-size: 20rpx;
font-weight: normal;
color: #aaa;
}
.cal-center .text-no-month {
color: #aaa;
}
.cal-center .timeout {
background-color: #f2f2f2;
}
.cal-center .timeout .num-grid {
color: #aaa;
}
/* 当日有数据 */
.data-has {
position: relative;
font-family: "icon";
font-size: inherit;
font-style: normal;
}
.data-has::before {
position: absolute;
content: '\e699';
right: 1rpx;
bottom: 0rpx;
color: #999;
font-size: 22rpx;
}
.cube.data-checked.data-has::before {
color: #fff;
}
/* 当日有预约 */
.join-has {
position: relative;
font-family: "icon";
font-size: inherit;
font-style: normal;
}
.join-has::before {
position: absolute;
content: '\e6c0';
right: 1rpx;
bottom: 0rpx;
color: #999;
font-size: 24rpx;
}
.cube.data-checked.join-has::before {
color: #fff;
}
/* 选中某日 */
.cube.data-checked {
position: relative;
display: flex;
justify-content: center;
align-items: center;
color: #fff !important;
}
.cube.data-checked .text-no-month {
color: #fff !important;
}
================================================
FILE: miniprogram/cmpts/public/calendar/date_select/date_select_cmpt.js
================================================
const dataHelper = require('../../../../helper/data_helper.js');
const pageHelper = require('../../../../helper/page_helper.js');
const timeHelper = require('../../../../helper/time_helper.js');
Component({
options: {
addGlobalClass: true
},
/**
* 组件的属性列表
*/
properties: {
start: { // 开始日期
type: String,
value: '',
},
end: { // 结束日期
type: String,
value: ''
},
selected: { // 当前选中日期
type: String,
observer: function (newVal, oldVal) {
if (newVal != oldVal) {
let month = timeHelper.timestamp2Time(timeHelper.time2Timestamp(newVal), 'Y年M月');
this.setData({ month });
}
}
},
toView: { //跳转到的view
type: String,
val: '',
}
},
/**
* 组件的初始数据
*/
data: {
range: [],
month: '',
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () { },
ready: function () {
if (!this.data.selected)
this.setData({ selected: timeHelper.time('Y-M-D') });
else
this.setData({ toView: 'day-' + this.data.selected });
this.init();
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
init: function () {
let start = this.data.start;
let end = this.data.end;
if (!start) start = timeHelper.time('Y-M-D');
if (!end) end = timeHelper.time('Y-M-D', 86400 * 15);
let range = [];
let startTime = timeHelper.time2Timestamp(start);
let endTime = timeHelper.time2Timestamp(end);
for (let k = startTime; k <= endTime;) {
let day = timeHelper.timestamp2Time(k, 'Y-M-D');
let month = timeHelper.timestamp2Time(k, 'Y年M月');
let node = {
day,
show: this._fmtShow(day),
week: this._fmtWeek(day),
month
}
range.push(node)
k += 86400 * 1000
}
this.setData({ range });
},
bindTap: function (e) {
},
_fmtShow: function (day) {
return day.split('-')[2];
},
_fmtWeek: function (day) {
if (day == timeHelper.time('Y-M-D')) return '今天';
day = timeHelper.week(day);
day = day.replace('周', '');
return day;
},
bindTap: function (e) {
let selected = pageHelper.dataset(e, 'day');
let month = pageHelper.dataset(e, 'month');
this.setData({ selected, month });
this.triggerEvent('select', selected);
}
},
})
================================================
FILE: miniprogram/cmpts/public/calendar/date_select/date_select_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/calendar/date_select/date_select_cmpt.wxml
================================================
{{month||'2022年'}}
{{item.week}}
{{item.show}}
================================================
FILE: miniprogram/cmpts/public/calendar/date_select/date_select_cmpt.wxss
================================================
@import "../calendar_comm/din.wxss";
.date-cmpt {
width: 100%;
}
.date-cmpt .month {
width: 100%;
padding: 10rpx 15rpx;
font-size: 32rpx;
color: #000;
font-weight: bold;
font-family: 'din';
}
.date-select {
white-space: nowrap;
}
.date-select .item {
margin: 10rpx 10rpx 10rpx 0rpx;
text-align: center;
font-size: 24rpx;
line-height: 55rpx;
height: 110rpx;
width: 85rpx;
border: 1rpx solid #ddd;
border-radius: 17rpx;
display: inline-block;
}
.date-select .item:first-child {
margin-left: 5rpx;
}
.date-select .item .date {
font-size: 32rpx;
color: #000;
font-family: 'din';
}
.date-select .item .week {
font-size: 24rpx;
color: #666;
}
.date-select .item.cur {
background-color: #f37445;
}
.date-select .item.cur .week,
.date-select .item.cur .date {
color: #fff;
}
================================================
FILE: miniprogram/cmpts/public/calendar/time_select/time_select_cmpt.js
================================================
const dataHelper = require('../../../../helper/data_helper.js');
const pageHelper = require('../../../../helper/page_helper.js');
const timeHelper = require('../../../../helper/time_helper.js');
Component({
options: {
addGlobalClass: true
},
/**
* 组件的属性列表
*/
properties: {
day: {
type: String,
value: '', // 当前日期
},
startTimeStep: {
type: Number,
value: 0, // 开始时间,把每天划分为48个时间段
},
endTimeStep: {
type: String,
value: 47, // 结束时间,把每天划分为48个时间段
},
used: { // 已选择
type: Array,
value: [], // {title,start,end,url=支持true或者跳转地址}
},
usedPos: { // 已约的标题位置 first / mid
type: String,
value: 'first'
},
},
/**
* 组件的初始数据
*/
data: {
times: [], //时间段 48个
selectedStart: '',
selectedEnd: '',
selectedEndPoint: '',
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () { },
ready: function () {
this.init();
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
init: function () {
let now = timeHelper.time('Y-M-D h:m');
let times = this.data.times;
let day = this.data.day;
if (!day) day = timeHelper.time('Y-M-D');
this.setData({
day
});
// 初始化
if (times == 0) {
for (let k = this.data.startTimeStep; k <= this.data.endTimeStep; k++) {
let start = '';
let end = '';
let title = '';
let clock = Math.trunc(k / 2);
if (k % 2 == 0) {
start = dataHelper.padLeft(clock, 2, '0') + ':00';
end = dataHelper.padLeft(clock, 2, '0') + ':30';
title = start;
}
else {
start = dataHelper.padLeft(clock, 2, '0') + ':30';
end = dataHelper.padLeft(clock + 1, 2, '0') + ':00';
title = '';
}
if (end == '24:00') end = '23:59';
let node = {
idx: k,
title,
start,
end,
used: false,
selected: false,
expire: (day + ' ' + start < now), //过期时间
}
times.push(node);
}
}
// 已约时间段
for (let k = 0; k < this.data.used.length; k++) {
let usedNode = this.data.used[k];
// 计算有占有几个时间段
let usedlen = 0;
for (let j = 0; j < times.length; j++) {
let node = times[j];
if (node.start >= usedNode.start && node.start <= usedNode.end) {
usedlen++;
}
}
if (usedlen <= 1) usedlen = 2;
usedlen = Math.round(usedlen / 2);
if (this.data.usedPos == 'first') usedlen = 1;
let curLen = 0;
for (let j = 0; j < times.length; j++) {
let node = times[j];
if (node.start == usedNode.start) {
node.used = usedNode.url || 'no';
node.usedFirst = true;
curLen++;
if (curLen == usedlen) node.usedText = usedNode.title;
}
else if (node.start >= usedNode.start && node.start <= usedNode.end) {
node.used = usedNode.url || 'no';
node.usedFirst = false;
curLen++;
if (curLen == usedlen) node.usedText = usedNode.title;
}
}
}
this.setData({ times });
},
bindSelectTap: function (e) {
// 选择
let idx = pageHelper.dataset(e, 'idx');
let timeNode = this.data.times[idx];
let selected = timeNode.start;
// 已选择
let used = timeNode.used;
if (used) {
if (used === true)
return;
else {
return wx.navigateTo({
url: used,
});
}
}
// 过期
let expire = timeNode.expire;
if (expire) return;
let selectedStart = this.data.selectedStart;
let selectedEnd = this.data.selectedEnd;
let times = this.data.times;
// 区间内直接干掉
if (selected >= selectedStart && selected <= selectedEnd) {
selectedStart = '';
selectedEnd = '';
for (let k = 0; k < times.length; k++) {
times[k].selected = false;
}
this.setData({ times, selectedStart, selectedEnd });
return;
}
if (!selectedStart && !selectedEnd) {
selectedStart = selected;
selectedEnd = selected;
}
if (selected < selectedStart) selectedStart = selected;
if (selected > selectedEnd) selectedEnd = selected;
// 选中
for (let k = 0; k < times.length; k++) {
if (times[k].start >= selectedStart && times[k].start <= selectedEnd) {
times[k].selected = true;
}
}
// 取得结束时间点
let selectedEndPoint = '';
for (let k = 0; k < times.length; k++) {
if (times[k].start == selectedEnd) {
selectedEndPoint = times[k].end;
}
}
this.setData({ times, selectedStart, selectedEnd, selectedEndPoint });
},
bindSumbitTap: function (e) {
let start = this.data.selectedStart;
let end = this.data.selectedEnd;
let endPoint = this.data.selectedEndPoint;
if (!start || !end || !endPoint) return;
this.triggerEvent('select', { start, end, endPoint });
}
}
})
================================================
FILE: miniprogram/cmpts/public/calendar/time_select/time_select_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/calendar/time_select/time_select_cmpt.wxml
================================================
{{item.title}}
{{item.usedText}}
确认预约 ({{selectedStart}}~{{selectedEndPoint}})
================================================
FILE: miniprogram/cmpts/public/calendar/time_select/time_select_cmpt.wxss
================================================
@import "../calendar_comm/din.wxss";
.time-table {
width: 100%;
padding: 0 5rpx;
}
.time-table .table-inner {
width: 100%;
font-size: 28rpx;
}
.time-table .list {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
}
.time-table .list .item {
color: #000;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
height: 42rpx;
padding: 0
}
.time-table .list .item .left {
background-color: #f5fafe;
height: inherit;
width: 140rpx;
text-align: center;
padding-top: 4rpx;
border-right: 1rpx solid #ddd;
border-left: 1rpx solid #ddd;
font-size: 30rpx;
font-weight: normal !important;
font-family: 'din';
}
.time-table .list .item:last-child .left {
border-bottom: 1rpx solid #ddd;
}
.time-table .list .item .left.top {
border-top: 1rpx solid #ddd !important;
border-bottom: 0 !important;
}
.time-table .list .item .right {
flex: 1;
height: inherit;
border-right: 1rpx solid #ddd;
border-top: 1rpx solid #ddd;
background-color: #fff;
color: #444;
padding: 0 10rpx;
width: 100%;
text-align: center;
}
.time-table .list .item:last-child .right {
border-bottom: 1rpx solid #ddd !important;
}
.time-table .list .item:first-child .right {
border-top: 1rpx solid #ddd !important;
}
.time-table .list .item .right.selected {
border-top: 1rpx solid #dfffd8;
background-color: #dfffd8;
}
.time-table .list .item .right.selected-first {
border-top: 1rpx solid #ddd !important;
}
.time-table .list .item .right.expire {
background-color: #f5f5f5;
color: #999;
}
.time-table .list .item .right.used,
.time-table .list .item .right.used-first {
border-top: 0rpx solid #E0EBFE;
background-color: #E0EBFE;
color: #f37445;
font-size: 28rpx;
border-left: 6rpx solid #1A65FF;
text-align: left;
}
.time-table .list .item .right.used-first {
border-top: 1rpx solid #ddd !important;
}
.time-submit {
width: 100%;
position: fixed;
bottom: 15rpx;
padding: 20rpx 30rpx;
z-index: 999999;
}
.time-submit .inner {
width: 100%;
background-color: #999;
font-size: 36rpx;
border-radius: 15rpx;
padding: 15rpx 0;
text-align: center;
color: #fff;
}
================================================
FILE: miniprogram/cmpts/public/checkbox/checkbox_cmpt.js
================================================
Component({
externalClasses: ['outside-picker-multi-class'],
/**
* 组件的属性列表
*/
properties: {
sourceData: { //源数组
type: Array,
value: [],
},
// 默认选中项的值数组
itemMulti: {
type: Array,
value: [],
observer: function (newVal, oldVal) {
if (Array.isArray(newVal) && Array.isArray(oldVal) && JSON.stringify(newVal) != JSON.stringify(oldVal)) {
console.log('checkbox observer');
this._fixDefaultVal();
}
}
},
disabled: { // 是否禁用
type: Boolean,
value: false,
},
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () { },
ready: function () {
this._fixDefaultVal();
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
bindChange: function (e) {
this.triggerEvent('select', e.detail.value);
},
_fixDefaultVal() { //传入数据不匹配的时候,修正父页面传入的的数组默认值
if (!Array.isArray(this.data.itemMulti)) {
this.triggerEvent('select', []);
}
if (this.data.itemMulti.length == 0) return;
let ret = [];
let sourceData = this.data.sourceData;
let itemMulti = this.data.itemMulti;
for (let k = 0; k < sourceData.length; k++) {
for (let j in itemMulti) {
if (sourceData[k] == itemMulti[j])
ret.push(itemMulti[j]);
}
}
this.triggerEvent('select', ret);
}
}
})
================================================
FILE: miniprogram/cmpts/public/checkbox/checkbox_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/checkbox/checkbox_cmpt.wxml
================================================
================================================
FILE: miniprogram/cmpts/public/checkbox/checkbox_cmpt.wxss
================================================
.checkbox-group {
width: 100%;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
padding: 0rpx 10rpx;
}
.checkbox-group .item {
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
line-height: 2.1;
min-height: 70rpx;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
}
.checkbox-group .item .item-label{
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.checkbox-group .item:last-child {
border-bottom: 0;
}
.checkbox-group .item:nth-child(odd) {
background-color: #fcfcfc;
}
.checkbox-group .item .item-checkbox {
margin-right: 20rpx;
padding-left: 10rpx;
}
================================================
FILE: miniprogram/cmpts/public/editor/editor_cmpt.js
================================================
const pageHelper = require('../../../helper/page_helper.js');
const dataHelper = require('../../../helper/data_helper.js');
const cloudHelper = require('../../../helper/cloud_helper.js');
const contentCheckHelper = require('../../../helper/content_check_helper.js');
const projectSetting = require('../../../setting/setting.js');
Component({
options: {
addGlobalClass: true
},
/**
* 组件的属性列表
*/
properties: {
nodeList: { // [{type:'text/img',val:'txt/cloudId'}]
type: Array,
value: [{
type: 'text',
val: ''
}]
},
viewMode: {
type: Boolean,
value: false,
},
isView: {
type: Boolean,
value: false,
},
upDirectDir: {
type: String,
value: '',
}
},
/**
* 组件的初始数据
*/
data: {
cur: -1,
},
lifetimes: {
attached: function () { },
ready: function () {
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
url: function (e) {
pageHelper.url(e, this);
},
setGlow(cur) {
this.setData({
cur
});
setTimeout(() => {
this.setData({
cur: -1
});
}, 1000);
},
bindAddTextTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let node = {
type: 'text',
val: ''
}
let nodeList = this.data.nodeList;
nodeList.splice(idx + 1, 0, node);
this.setData({
nodeList
});
this.setGlow(idx + 1);
},
bindAddImageTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let that = this;
wx.chooseMedia({
count: 8,
mediaType: ['image'],
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: async res => {
let nodeList = that.data.nodeList;
for (let k = 0; k < res.tempFiles.length; k++) {
let path = res.tempFiles[k].tempFilePath;
let size = res.tempFiles[k].size;
if (!contentCheckHelper.imgTypeCheck(path)) {
wx.hideLoading();
return pageHelper.showNoneToast('只能上传png、jpg、jpeg格式', 3000);
}
let maxSize = 20; //TODO setting
let imageMaxSize = 1024 * 1000 * maxSize;
console.log('IMGX SIZE=' + size + 'Byte,' + size / 1024 + 'K');
if (!contentCheckHelper.imgSizeCheck(size, imageMaxSize)) {
wx.hideLoading();
return pageHelper.showModal('图片大小不能超过 ' + maxSize + '兆');
}
if (!projectSetting.IS_DEMO && this.data.upDirectDir) {
wx.showLoading({ title: '上传中' });
path = await cloudHelper.transTempPicOne(path, this.data.upDirectDir, '', false);
wx.hideLoading();
}
let node = {
type: 'img',
val: path
};
nodeList.splice(idx + 1, 0, node);
}
that.setData({
nodeList
});
//that.setGlow(idx + 1);
}
})
},
bidnDeleteNodeTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let nodeList = this.data.nodeList;
if (this.data.nodeList.length == 1) return pageHelper.showNoneToast('至少需要一个内容框');
nodeList.splice(idx, 1);
this.setData({
nodeList
});
},
bindUpTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let nodeList = this.data.nodeList;
nodeList = dataHelper.arraySwap(nodeList, idx, idx - 1);
this.setData({
nodeList
});
pageHelper.anchor('editor-node-' + (idx - 1), this);
this.setGlow(idx - 1);
},
bindTopTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let nodeList = this.data.nodeList;
nodeList = dataHelper.arrayTop(nodeList, idx);
this.setData({
nodeList
});
pageHelper.anchor('editor-node-0', this);
this.setGlow(0);
},
bindBottomTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let nodeList = this.data.nodeList;
nodeList = dataHelper.arrayBottom(nodeList, idx);
this.setData({
nodeList
});
pageHelper.anchor('editor-node-' + (nodeList.length - 1), this);
this.setGlow(nodeList.length - 1);
},
bindDownTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let nodeList = this.data.nodeList;
nodeList = dataHelper.arraySwap(nodeList, idx, idx + 1);
this.setData({
nodeList
});
pageHelper.anchor('editor-node-' + (idx + 1), this);
this.setGlow(idx + 1);
},
bindTextareaInput: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let nodeList = this.data.nodeList;
let node = nodeList[idx];
if (node.type == 'text') {
node.val = e.detail.value;
nodeList[idx] = node;
/*
this.setData({
nodeList
});*/
}
},
getNodeList: function (e) {
let nodeList = this.data.nodeList;
// 校验是否填写了内容
let imgCnt = 0;
let textCnt = 0;
for (let k = 0; k < nodeList.length; k++) {
if (nodeList[k].type == 'img' && nodeList[k].val.trim() != '') imgCnt++;
if (nodeList[k].type == 'text' && nodeList[k].val.trim() != '') textCnt++;
}
if ((imgCnt + textCnt) == 0) {
return [];
}
return this.data.nodeList;
},
}
})
================================================
FILE: miniprogram/cmpts/public/editor/editor_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/editor/editor_cmpt.wxml
================================================
添加文本
添加图片
================================================
FILE: miniprogram/cmpts/public/editor/editor_cmpt.wxss
================================================
.editor-tab {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
z-index: 999;
line-height: 3;
height: 70rpx;
margin-top: 40rpx;
}
.editor-tab .item.cur::after {
width: 100%;
position: absolute;
bottom: 10rpx;
left: 0rpx;
height: 6rpx;
content: '';
background-color: #ff5858;
}
.iconfont {
font-size: 32rpx;
font-style: normal;
}
.editor-wrapper {
padding: 30rpx;
box-sizing: border-box;
}
.editor-add-wrapper {
display: flex;
flex-direction: row;
align-items: center;
margin: 0 auto;
font-size: 30rpx;
width: 100%;
justify-content: center;
}
.editor-add-wrapper .editor-add-wrapper-view {
width: 250rpx;
line-height: 2;
text-align: center;
}
.editor-add-wrapper .iconfont {
padding: 20rpx;
color: #666;
font-size: 48rpx;
box-sizing: border-box;
}
.editor-node-wrapper {
position: relative;
width: 100%;
margin-top: 10rpx;
margin-bottom: 10rpx;
border: 1rpx dashed #999;
background: #fff;
}
.editor-node-wrapper.is-view {
border: 1rpx solid #fff;
border-radius: 10rpx;
}
.editor-node-wrapper.cur {
animation: glow 800ms ease-out infinite alternate;
}
@keyframes glow {
0% {
box-shadow: 0 0 5px rgba(252, 94, 94, .2), inset 0 0 5px rgba(252, 94, 94, .1), 0 0px 0 #d70c19;
}
100% {
border-color: #d70c19;
box-shadow: 0 0 20px rgba(252, 94, 94, .6), inset 0 0 10px rgba(252, 94, 94, .4), 0 0px 0 #d70c19;
}
}
.editor-delete {
position: absolute;
z-index: 999;
display: flex;
text-align: center;
align-items: center;
justify-content: center;
top: -30rpx;
right: 0rpx;
color: #999;
background: hsla(0, 0%, 100%, .95);
border-radius: 40rpx;
padding: 15rpx;
box-shadow: 0 0 5px rgba(0, 0, 0, .1);
}
.editor-delete .iconfont {
padding: 0 20rpx;
}
.editor-wrapper .editor-node-wrapper .editor-textarea {
margin: 0;
display: block;
width: 100%;
line-height: 1.5;
padding: 35rpx 20rpx 20rpx;
min-height: 240rpx;
font-size: 32rpx;
}
.editor-images {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 656rpx;
border: 2rpx solid #e4e7ed;
background: #fff;
padding: 20rpx;
min-height: 2.5em;
}
.editor-images .item-image {
display: flex;
align-items: center;
justify-content: center;
margin: 5rpx;
width: 150rpx;
height: 150rpx;
box-sizing: border-box;
}
.editor-images .editor-images-add {
border: 4rpx dashed #c0c4cc;
color: #c0c4cc;
}
.editor-image {
display: block;
width: 100%;
}
================================================
FILE: miniprogram/cmpts/public/form/form_set/field/form_set_field.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const dataHelper = require('../../../../../helper/data_helper.js');
const helper = require('../../../../../helper/helper.js');
const formSetHelper = require('../../form_set_helper.js');
let _parentFormSet = null;
Page({
/**
* 页面的初始数据
*/
data: {
index: -1, // 父页面索引 -1则为新加
typeOptions: formSetHelper.getTypeOptions(),
onlySetOptions: formSetHelper.getOnlySetOptions(),
maxOptions: dataHelper.getSelectOptions('0=0个字,1=1个字,2=2个字,3=3个字,4=4个字,5=5个字,6=6个字,7=7个字,8=8个字,9=9个字,10=10个字,11=11个字,12=12个字,13=13个字,14=14个字,15=15个字,15=15个字,16=16个字,17=17个字,18=18个字,19=19个字,20=20个字,25=25个字,30=30个字,40=40个字,50=50个字,100=100个字,200=200个字,500=500个字,1000=1000个字,2000=2000个字'),
minOptions: dataHelper.getSelectOptions('0=0个字,1=1个字,2=2个字,3=3个字,4=4个字,5=5个字,6=6个字,7=7个字,8=8个字,9=9个字,10=10个字,11=11个字,12=12个字,13=13个字,14=14个字,15=15个字,15=15个字,16=16个字,17=17个字,18=18个字,19=19个字,20=20个字,25=25个字,30=30个字,40=40个字,50=50个字,100=100个字,200=200个字,500=500个字'),
checkBoxLimitOptions: dataHelper.getSelectOptions('0=0项,1=1项,2=2项,3=3项,4=4项,5=5项,6=6项,7=7项,8=8项,9=9项,10=10项,11=11项,12=12项,13=13项,14=14项,15=15项,16=16项,17=17项,18=18项,19=19项,20=20项'),
onlySetDesc: '',
// 基本属性
formMark: '',
formType: 'text',
formTitle: '',
formDesc: '',
formMust: true,
formMax: 50,
formMin: 0,
formOnlySet: {
mode: 'all',
cnt: -1
},
// type=select
formSelectOptions: ['', ''],
// type=checkbox
formCheckBoxLimit: 2,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.setData({
formMark: formSetHelper.mark(),
onlySetDesc: formSetHelper.getOnlySetDesc(this.data.formOnlySet)
});
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
_parentFormSet = parent.selectComponent("#form-set");
if (options && helper.isDefined(options.idx)) {
let index = options.idx;
let fields = _parentFormSet.get();
let node = fields[index];
if (!node.mark) node.mark = formSetHelper.mark();
this.setData({
index,
formMark: node.mark,
formType: node.type,
formTitle: node.title,
formDesc: node.desc,
formMust: node.must,
formMax: node.max,
formMin: node.min,
formOnlySet: node.onlySet,
formSelectOptions: node.selectOptions,
formCheckBoxLimit: node.checkBoxLimit,
onlySetDesc: formSetHelper.getOnlySetDesc(node.onlySet)
});
wx.setNavigationBarTitle({
title: '字段编辑',
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
// 重复次数
bindOnlySetCmpt: function (e) {
let formOnlySet = {};
formOnlySet.mode = e.detail[0];
formOnlySet.cnt = e.detail[1];
this.setData({
formOnlySet,
onlySetDesc: formSetHelper.getOnlySetDesc(formOnlySet)
});
},
bindDelTap: function (e) {
if (this.data.index == -1) return;
let callback = () => {
let fields = _parentFormSet.get();
fields.splice(this.data.index, 1);
_parentFormSet.set(fields);
wx.navigateBack();
}
pageHelper.showConfirm('确定要删除当前字段吗?', callback);
},
bindSubmitTap: function (e) {
if (this.data.formTitle.length < 1) return pageHelper.showModal('字段名称必填哦');
if (this.data.formTitle.length > 60) return pageHelper.showModal('字段名称必不能超过60个字');
if (this.data.formDesc.length > 30) return pageHelper.showModal('填写说明不能超过30个字');
if (this.data.formType.length < 1) return pageHelper.showModal('字段填写类型必须选择哦');
let formType = this.data.formType;
if (formType == 'select' || formType == 'checkbox') {
// 下拉框
let formSelectOptions = this.data.formSelectOptions;
for (let k = 0; k < formSelectOptions.length; k++) {
if (formSelectOptions[k].length < 1) {
return pageHelper.showModal('选项' + (Number(k) + 1) + '还没填哦');
}
if (formSelectOptions[k].length > 30) {
return pageHelper.showModal('选项' + (Number(k) + 1) + '不能超过30个字,精简一点!');
}
}
this.data.formMax = 50;
this.data.formMin = 0;
if (formType == 'select') this.data.formCheckBoxLimit = 2;
} else if (formType == 'mobile') {
//非本类型的排除
this.data.formSelectOptions = ['', ''];
this.data.formCheckBoxLimit = 2;
this.data.formMax = 11;
this.data.formMin = 11;
} else {
//非本类型的排除
this.data.formSelectOptions = ['', ''];
this.data.formCheckBoxLimit = 2;
if (formType != 'text' && formType != 'textarea' && formType != 'int' && formType != 'digit') {
this.data.formMax = 50;
this.data.formMin = 0;
}
}
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
let fields = _parentFormSet.get();
let node = {
mark: this.data.formMark,
title: this.data.formTitle,
desc: this.data.formDesc,
type: this.data.formType,
must: this.data.formMust,
max: Number(this.data.formMax),
min: Number(this.data.formMin),
onlySet: this.data.formOnlySet,
selectOptions: this.data.formSelectOptions,
checkBoxLimit: Number(this.data.formCheckBoxLimit),
};
node = formSetHelper.initFieldOne(node);
if (this.data.index == -1) {
fields.push(node); //新的
} else {
fields[this.data.index] = node; //修改的
}
_parentFormSet.set(fields);
wx.navigateBack();
},
switchModel: function (e) {
pageHelper.switchModel(this, e, 'bool');
},
bindSelectOptionsBlur: function (e) {
// 多选项目的输入
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail.value.trim();
let formSelectOptions = this.data.formSelectOptions;
formSelectOptions[idx] = val;
/*
this.setData({
formSelectOptions
});*/
},
bindDelSelectOptionsTap: function (e) {
let formSelectOptions = this.data.formSelectOptions;
if (formSelectOptions.length <= 2) return pageHelper.showModal('至少2个选项');
let callback = () => {
let idx = pageHelper.dataset(e, 'idx');
formSelectOptions.splice(idx, 1);
this.setData({
formSelectOptions
});
}
pageHelper.showConfirm('确定删除该项吗?', callback);
},
bindAddSelectOptionsTap: function (e) {
let formSelectOptions = this.data.formSelectOptions;
if (formSelectOptions.length >= 20) return pageHelper.showModal('最多可以添加20个选项');
formSelectOptions.push('');
this.setData({
formSelectOptions
});
}
})
================================================
FILE: miniprogram/cmpts/public/form/form_set/field/form_set_field.json
================================================
{
"usingComponents": {
"cmpt-picker-multi": "/cmpts/public/picker_multi/picker_multi_cmpt"
},
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark",
"navigationBarTitleText": "后台-字段添加"
}
================================================
FILE: miniprogram/cmpts/public/form/form_set/field/form_set_field.wxml
================================================
================================================
FILE: miniprogram/cmpts/public/form/form_set/field/form_set_field.wxss
================================================
.main {
margin-bottom: 150rpx;
}
.oprt {
display: flex;
width: 100%;
justify-content: space-around;
}
.oprt button {
width: 45%;
}
.select-close {
width: 60rpx;
font-size: 40rpx !important;
font-weight: bold;
text-align: right;
}
.title-desc {
padding-left: 24rpx;
color: #aaa;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #eee;
}
.title-info {
margin-left: 4rpx;
color: #aaa;
font-weight: normal;
font-size: 24rpx;
}
================================================
FILE: miniprogram/cmpts/public/form/form_set/form_set_cmpt.js
================================================
const pageHelper = require('../../../../helper/page_helper.js');
const dataHelper = require('../../../../helper/data_helper.js');
Component({
options: {
addGlobalClass: true
},
/**
* 组件的属性列表
*/
properties: {
fields: {
type: Array,
value: [],
},
},
/**
* 组件的初始数据
*/
data: {
cur: -1,
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () {
},
ready: function () {
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
setGlow(cur) {
this.setData({
cur
});
setTimeout(() => {
this.setData({
cur: -1
});
}, 800);
},
url: function (e) {
pageHelper.url(e, this);
},
set: function (fields) {
this.setData({
fields
});
this.triggerEvent('formset', fields);
},
get: function () {
return this.data.fields;
},
bindEditTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let edit = pageHelper.dataset(e, 'edit');
if (!edit) {
return pageHelper.showNoneToast('该字段不可编辑和删除');
}
wx.navigateTo({
url: '/cmpts/public/form/form_set/field/form_set_field?idx=' + idx,
});
},
bindUpTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let fields = this.data.fields;
dataHelper.arraySwap(fields, idx, idx - 1);
this.setData({
fields
});
this.setGlow(idx - 1);
this.triggerEvent('formset', fields);
},
bindDownTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let fields = this.data.fields;
dataHelper.arraySwap(fields, idx, idx + 1);
this.setData({
fields
});
this.setGlow(idx + 1);
this.triggerEvent('formset', fields);
}
}
})
================================================
FILE: miniprogram/cmpts/public/form/form_set/form_set_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/form/form_set/form_set_cmpt.wxml
================================================
{{item.title}}
单行文本,
日期,
时间点,
单项选择,
多项选择,
开关选择,
省市区,
多行文本,
年份,
月份,
整数数字,
小数数字,
身份证,
手机,
必填
选填
添加新字段
================================================
FILE: miniprogram/cmpts/public/form/form_set/form_set_cmpt.wxss
================================================
.picker-set {
line-height: 100rpx;
font-size: 24rpx;
width: 100%;
text-align: right;
color: #999;
width: 210rpx;
}
.form-group .picker-set {
padding-right: 40rpx;
overflow: hidden;
position: relative;
}
.form-group.cur {
animation: glow 800ms linear 1 alternate;
}
@keyframes glow {
0% {
background-color: #ececec;
}
100% {
background-color: #fff;
}
}
.form-group-active{
background-color: #ececec;
}
.form-group .title {
font-weight: normal;
color: #333;
flex: 1;
padding-right: 10rpx;
}
.form-group .field-oprt {
color: #777;
background: hsla(0, 0%, 100%, .95);
border-radius: 5rpx;
box-shadow: 0 0 5px rgba(0, 0, 0, .1);
display: flex;
text-align: center;
align-items: center;
justify-content: center;
margin-right: 15rpx;
}
.form-group .field-oprt>view{
width:70rpx;
height:55rpx;
display: flex;
text-align: center;
align-items: center;
justify-content: center;
font-size:36rpx;
}
================================================
FILE: miniprogram/cmpts/public/form/form_set_helper.js
================================================
/**
* 配置格式
* mark
* title:
* type:
* desc:
* min:
* max:
* must:
* checkBoxLimit:2
* onlySet:{mode:'all',cnt:-1}
* selectOptions:['','']
* def
*/
const dataHelper = require('../../../helper/data_helper.js');
const pageHelper = require('../../../helper/page_helper.js');
const helper = require('../../../helper/helper.js');
const validate = require('../../../helper/validate.js');
function initFieldOne(field) {
return initFields([field])[0];
}
function initFields(defaultFields = null) {
let fields = dataHelper.deepClone(defaultFields);
if (fields && Array.isArray(fields)) {
for (let k = 0; k < fields.length; k++) {
let curForm = fields[k];
// 补充编号
if (!curForm['mark']) curForm['mark'] = mark();
if (!helper.isDefined(curForm['type'])) curForm['type'] = 'text';
if (!helper.isDefined(curForm['title'])) curForm['title'] = '姓名';
if (!helper.isDefined(curForm['desc'])) curForm['desc'] = '';
if (!helper.isDefined(curForm['must'])) curForm['must'] = false;
if (!helper.isDefined(curForm['def'])) curForm['def'] = null; //默认值
if (!helper.isDefined(curForm['edit'])) curForm['edit'] = true; //是否可编辑删除
// 默认长度
let max = 50;
let min = 0;
switch (fields[k].type) {
case 'textarea':
max = 500;
break;
case 'image':
max = 8;
break;
case 'digit':
case 'int':
max = 10;
break;
}
// 长度
if (!helper.isDefined(curForm['max'])) curForm['max'] = max;
if (!helper.isDefined(curForm['min'])) curForm['min'] = min;
// 固定长度
if (helper.isDefined(curForm['len'])) {
curForm['max'] = curForm['len'];
curForm['min'] = curForm['len'];
delete curForm['len'];
}
// 图片张数不超过8
if (fields[k].type == 'image') {
if (curForm['max'] > 8) curForm['max'] = 8;
if (curForm['min'] > 8) curForm['min'] = 8;
}
if (fields[k].type == 'mobile') {
curForm['max'] = 11;
curForm['min'] = 11;
}
// 唯一性
if (!helper.isDefined(curForm['onlySet'])) curForm['onlySet'] = {
mode: 'all',
cnt: -1
};
// 可选项
if (!helper.isDefined(curForm['selectOptions'])) {
if (fields[k].type == 'select' || fields[k].type == 'checkbox' || fields[k].type == 'radio')
curForm['selectOptions'] = ['是', '否'];
else
curForm['selectOptions'] = ['', ''];
}
if (!helper.isDefined(curForm['checkBoxLimit'])) curForm['checkBoxLimit'] = 2;
}
}
return fields || [
{
mark: mark(),
type: 'text',
title: '姓名',
desc: '您的姓名',
must: true,
min: 0,
max: 30,
onlySet: {
mode: 'all',
cnt: -1
},
selectOptions: ['', ''],
checkBoxLimit: 2,
def: null,
edit:false
},
];
}
// 函数参数必须是字符串,因为二代身份证号码是十八位,而在javascript中,十八位的数值会超出计算范围,造成不精确的结果,导致最后两位和计算的值不一致,从而该函数出现错误。
// 详情查看javascript的数值范围
function checkIDCard(idcode) {
if (idcode.length != 18) return false;
// 加权因子
var weight_factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 校验码
var check_code = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
var code = idcode + "";
var last = idcode[17]; //最后一位
var seventeen = code.substring(0, 17);
// ISO 7064:1983.MOD 11-2
// 判断最后一位校验码是否正确
var arr = seventeen.split("");
var len = arr.length;
var num = 0;
for (var i = 0; i < len; i++) {
num = num + arr[i] * weight_factor[i];
}
// 获取余数
var resisue = num % 11;
var last_no = check_code[resisue];
// 格式的正则
// 正则思路
/*
第一位不可能是0
第二位到第六位可以是0-9
第七位到第十位是年份,所以七八位为19或者20
十一位和十二位是月份,这两位是01-12之间的数值
十三位和十四位是日期,是从01-31之间的数值
十五,十六,十七都是数字0-9
十八位可能是数字0-9,也可能是X
*/
var idcard_patter = /^[1-9][0-9]{5}([1][9][0-9]{2}|[2][0][0|1][0-9])([0][1-9]|[1][0|1|2])([0][1-9]|[1|2][0-9]|[3][0|1])[0-9]{3}([0-9]|[X])$/;
// 判断格式是否正确
var format = idcard_patter.test(idcode);
// 返回验证结果,校验码和格式同时正确才算是合法的身份证号码
return last === last_no && format ? true : false;
}
// 必填提示
function getMustHint(type) {
if (type == 'image') return '请上传';
let arr = ['select', 'date', 'month', 'hourminute', 'time', 'checkbox', 'radio','switch', 'area'];
if (arr.includes(type))
return '请选择';
else
return '请填写';
}
// form 数据校验
function checkForm(fields, forms, that) {
for (let k = 0; k < fields.length; k++) {
delete fields[k].focus;
}
for (let k = 0; k < fields.length; k++) {
let type = fields[k].type;
let title = '「' + fields[k].title + '」';
let val = forms[k].val;
// 必填
let hintOprt = getMustHint(type); //提示动作
if (fields[k].must && type != 'switch' && (!helper.isDefined(val) || val.length == 0)) {
fields[k].focus = hintOprt + title;
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(hintOprt + '' + title);
}
// 填写后,字符串最大长度
if (val.length > 0 && (type == 'text' || type == 'textarea' || type == 'int' || type == 'digit')) {
if (fields[k].max == fields[k].min) {
let len = fields[k].max;
if (val.length != len) {
fields[k].focus = title + ' 字数必须为' + len + '位';
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(title + ' 字数必须为' + len + '位');
}
}
else {
if (val.length > fields[k].max) {
fields[k].focus = title + ' 字数不能多于' + fields[k].max + '位';
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(title + ' 字数不能多于' + fields[k].max + '位');
}
if (val.length < fields[k].min) {
fields[k].focus = title + ' 字数不能少于' + fields[k].min + '位';
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(title + ' 字数不能少于' + fields[k].min + '位');
}
}
}
// 传图后,字符串最大长度
if (val.length > 0 && (type == 'image')) {
if (fields[k].max == fields[k].min) {
let len = fields[k].max;
if (val.length != len) {
fields[k].focus = title + ' 张数必须为' + len + '张';
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(title + ' 张数必须为' + len + '张');
}
}
else {
if (val.length > fields[k].max) {
fields[k].focus = title + ' 张数不能多于' + fields[k].max + '张';
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(title + ' 张数不能多于' + fields[k].max + '张');
}
if (val.length < fields[k].min) {
fields[k].focus = title + ' 张数不能少于' + fields[k].min + '张';
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(title + ' 张数不能少于' + fields[k].min + '张');
}
}
}
switch (type) {
case 'tag': { // 标签格式化
forms[k].val = dataHelper.fmtTag(val);
break;
}
case 'mobile': {
if (val.length > 0 && val.length != 11) {
fields[k].focus = '请填写正确的 ' + title;
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal('请填写正确的 ' + title);
}
break;
}
case 'switch': {
// TODO 是否要做判断
break;
}
case 'idcard': {
if (val.length > 0 && !checkIDCard(val)) {
fields[k].focus = '请填写正确的 ' + title;
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal('请填写正确的 ' + title);
}
break;
}
case 'checkbox': {
if (val.length > 0 && val.length < fields[k].checkBoxLimit) {
fields[k].focus = title + ' 至少选中' + fields[k].checkBoxLimit + '项';
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal(title + ' 至少选中' + fields[k].checkBoxLimit + '项');
}
break;
}
case 'date': {
if (validate.checkDate(val)) {
fields[k].focus = '请填写正确的 ' + title;
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal('请填写正确的 ' + title);
}
break;
}
case 'year': {
if (validate.checkYear(val)) {
fields[k].focus = '请填写正确的 ' + title;
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal('请填写正确的 ' + title);
}
break;
}
case 'month': {
if (validate.checkYearMonth(val)) {
fields[k].focus = '请填写正确的 ' + title;
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal('请填写正确的 ' + title);
}
break;
}
case 'hourminute': {
if (validate.checkHourMinute(val)) {
fields[k].focus = '请填写正确的 ' + title;
pageHelper.anchor('form' + forms[k].mark, that);
return pageHelper.showModal('请填写正确的 ' + title);
}
break;
}
case 'int': {
if (validate.checkInt(val)) {
fields[k].focus = title + '必须为数字';
return pageHelper.showModal(title + '必须为数字');
}
break;
}
case 'digit': {
if (validate.checkDigit(val)) {
fields[k].focus = title + '必须为数字或小数';
return pageHelper.showModal(title + '必须为数字或小数');
}
break;
}
}
}
return true;
}
function mark() {
return dataHelper.genRandomAlpha(10).toUpperCase();
};
function getTypeOptions() {
//return dataHelper.getSelectOptions('text=单行文本,select=单项选择,checkbox=多项选择,switch=开关选择,textarea=多行文本,idcard=身份证号码,mobile=手机号码,date=日期 (年 月 日),month=月份,year=年份,hourminute=时间点,area=省市区,int=整数数字,digit=带小数点的数字');
return dataHelper.getSelectOptions('text=单行文本,select=单项选择,checkbox=多项选择,switch=开关选择,textarea=多行文本,idcard=身份证号码,date=日期 (年 月 日),month=月份,year=年份,hourminute=时间点,area=省市区,int=整数数字,digit=带小数点的数字');
}
// 重复性规则
function getOnlySetOptions() {
let mode = dataHelper.getSelectOptions('all=本项目全程重复次数,clock=按每一时段限制重复次数,day=按每天限制重复次数');
let list = [];
for (let k = 0; k < mode.length; k++) {
let node = {};
node.label = mode[k].label;
node.val = mode[k].val;
let children = [];
if (k == 0) {
children.push({
label: '不限制重复次数',
val: -1
});
}
for (let j = 1; j <= 30; j++) {
let childNode = {};
if (j == 1)
childNode.label = '仅可填写' + j + '次';
else
childNode.label = '可重复' + j + '次';
childNode.val = j
children.push(childNode);
}
node.children = children;
list.push(node);
}
return list;
}
// 重复性规则的表述
function getOnlySetDesc(rule) {
let ret = '';
switch (rule.mode) {
case 'all':
ret = rule.cnt > 0 ? '本项目全程可重复' + rule.cnt + '次' : '本项目全程不限制重复次数';
break;
case 'day':
ret = '每天可重复' + rule.cnt + '次';
break;
case 'clock':
ret = '每一时段可重复' + rule.cnt + '次';
break;
}
if (rule.cnt == 1) ret = ret.replace(/可重复/g, '仅可填写');
return ret;
}
module.exports = {
checkForm,
mark,
initFieldOne,
initFields,
getTypeOptions,
getOnlySetOptions,
getOnlySetDesc
}
================================================
FILE: miniprogram/cmpts/public/form/form_show/content/form_show_content.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
formContent: [{
type: 'text',
val: '',
}]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
if (!options) return;
if (!options.cmptId || !options.cmptFormName) return;
let cmptId = '#' + options.cmptId;
let cmptFormName = options.cmptFormName;
let formContent = parent.selectComponent(cmptId).getOneFormVal(cmptFormName);
if (formContent.length == 0) {
formContent = [{ type: 'text', val: '' }];
}
this.setData({
cmptId,
cmptFormName,
formContent
});
let curPage = pageHelper.getPrevPage(1);
if (!curPage) return;
if (curPage.options && curPage.options.source == 'admin') {
wx.setNavigationBarColor({ //管理端顶部
backgroundColor: '#2499f2',
frontColor: '#ffffff',
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
},
model: function (e) {
pageHelper.model(this, e);
},
url: function (e) {
pageHelper.url(e, this);
},
bindSaveTap: function (e) {
// 获取富文本,如果没填写则为[]
let formContent = this.selectComponent("#contentEditor").getNodeList();
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
parent.selectComponent(this.data.cmptId).setOneFormVal(this.data.cmptFormName, formContent);
wx.navigateBack();
}
})
================================================
FILE: miniprogram/cmpts/public/form/form_show/content/form_show_content.json
================================================
{
"usingComponents": {
"cmpt-editor": "/cmpts/public/editor/editor_cmpt"
},
"navigationBarTitleText": "详细内容"
}
================================================
FILE: miniprogram/cmpts/public/form/form_show/content/form_show_content.wxml
================================================
不保存,返回
保存
================================================
FILE: miniprogram/cmpts/public/form/form_show/content/form_show_content.wxss
================================================
@import '../../../../../style/public/admin.wxss';
.main-admin {
width: 100%;
box-sizing: border-box;
padding: 30rpx 20rpx;
padding-bottom: 200rpx;
}
.form-group {
padding: 1rpx 1rpx;
overflow: hidden;
}
.oprt {
display: flex;
width: 100%;
justify-content: space-around;
}
.oprt button {
width: 45%;
}
.bottom-oprt {
position: fixed;
bottom: 0;
height: 130rpx;
background-color: #f8f8f8;
display: flex;
justify-content: space-around;
align-items: center;
border-top: 1rpx solid #ccc;
z-index: 99999;
}
================================================
FILE: miniprogram/cmpts/public/form/form_show/form_show_cmpt.js
================================================
const pageHelper = require('../../../../helper/page_helper.js');
const helper = require('../../../../helper/helper.js');
const cloudHelper = require('../../../../helper/cloud_helper.js');
const cacheHelper = require('../../../../helper/cache_helper.js');
const formSetHelper = require('../form_set_helper.js');
const validate = require('../../../../helper/validate.js');
const setting = require('../../../../setting/setting.js');
const CACHE_FORM_SHOW_KEY = 'FORM_SHOW_CMPT';
const CACHE_FORM_SHOW_TIME = 86400 * 365;
Component({
options: {
addGlobalClass: true
},
/**
* 组件的属性列表
*/
properties: {
mark: { // 组件标识,用于区分缓存
type: String,
value: '',
},
source: { // 来源 admin /user
type: String,
value: 'user',
},
fields: { // 表单字段属性{mark,val,type,must,selectOptions,desc,title}
type: Array,
value: [],
},
forms: { // 表单值
type: Array,
value: [], // {mark,title,val,type}
},
doShow: { //仅仅显示
type: Boolean,
value: false,
},
isConfirm: { //是否显示核对信息modal
type: Boolean,
value: true,
},
isCacheMatch: { //是否开启缓存匹配
type: Boolean,
value: true,
},
isDefMatch: { //是否开启缺省值匹配
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
cacheName: '',
isLoad: false,
showCheckModal: false,
mobileCheck: setting.MOBILE_CHECK
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () {
},
ready: function () {
if (this.data.isCacheMatch) {
let cacheName = CACHE_FORM_SHOW_KEY + '_' + this.data.mark;
this.setData({
cacheName
});
}
this._init();
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
reload: function () {
// 重新加载,如果没有设置扩展字段,则全部form属性清空
this._init();
},
_init: function () {
let fields = formSetHelper.initFields(this.data.fields);
let newForms = [];
for (let k = 0; k < fields.length; k++) {
let node = {};
node.mark = fields[k].mark;
node.title = fields[k].title;
node.type = fields[k].type;
// 判断是否有表单值(依次从表单值,缓存,默认值)
let val = this._getOneValForm(fields[k].mark, fields[k].title, fields[k].type);
if (val === null) val = '';
// 数据类型修正
val = this._fixType(fields[k].type, val);
node.val = val;
fields[k].val = val;
newForms.push(node);
}
this.setData({
forms: newForms,
fields,
isLoad: true
});
//this.triggerEvent('forms', newForms);
},
// 根据mark和type获取上次值或者缓存值或者缺省值
_getOneValForm: function (mark, title, type) {
if (type == 'line') return title;
let ret = null;
// **** 对传入的默认值匹配
let forms = this.data.forms;
if (!forms || !Array.isArray(forms)) forms = [];
for (let k = 0; k < forms.length; k++) {
if (forms[k].mark == mark && forms[k].type == type) { // 优先匹配mark
ret = forms[k].val;
break;
}
if (forms[k].title == title && forms[k].type == type) { // 再则匹配名称
ret = forms[k].val;
break;
}
if (type == 'mobile' && forms[k].type == 'mobile') {
ret = forms[k].val;
break;
}
if (type == 'idcard' && forms[k].type == 'idcard') {
ret = forms[k].val;
break;
}
}
// **** 对缓存匹配 图片和富文本不读取缓存
if (ret === null && this.data.isCacheMatch
&& (type != 'image' && type != 'content')) {
let caches = cacheHelper.get(this.data.cacheName);
if (caches && Array.isArray(caches)) {
for (let k = 0; k < caches.length; k++) {
if (caches[k].mark == mark && caches[k].type == type) { // 优先匹配mark
ret = caches[k].val;
break;
}
if (caches[k].title == title && caches[k].type == type) { // 再则匹配名称
ret = caches[k].val;
break;
}
if (type == 'mobile' && caches[k].type == 'mobile') {
ret = caches[k].val;
break;
}
if (type == 'idcard' && caches[k].type == 'idcard') {
ret = caches[k].val;
break;
}
}
}
}
// 缺省值匹配
if (ret === null && this.data.isDefMatch) {
let fields = this.data.fields;
for (let k = 0; k < fields.length; k++) {
if (fields[k].mark == mark
&& helper.isDefined(fields[k].def)
&& fields[k].def != null // 默认值为空
) {
ret = fields[k].def;
break;
}
}
}
return ret;
},
// 原始form没有对应值, 给予默认值; 或者类型不对,修正
_fixType: function (type, val) {
if (type == 'line') return val;
if (type != 'switch' && type != 'checkbox' && type != 'area' && type != 'content' && type != 'image') {
// switch(bool),checkbox(array), area(array), content(array) 不处理,其他做类型处理
if (typeof val === 'object' && !Array.isArray(val)) {
// 对象要被处理为空串,数组做trim不处理(typeof数组也是object)
val = '';
}
else
val = String(val).trim(); // 前后去空格
}
// 原始form 有对应值,但是类型不正确
switch (type) {
case 'image': {
// 不支持字符串缺省值
if (!Array.isArray(val)) return [];
break;
}
case 'content': {
// 支持字符串缺省值
if (typeof val === 'string') {
if (val)
return [{ type: 'text', val: val.trim() }];
else
return [];
}
if (!Array.isArray(val)) return [];
break;
}
case 'area': {
if (!Array.isArray(val) || val.length != 3) return ''; //TODO?
break;
}
case 'switch': {
if (typeof (val) != 'boolean') return true;
break;
}
case 'checkbox': {
if (!Array.isArray(val)) return [String(val).trim()]; //尝试转为数组来匹配
break;
}
case 'year': {
if (!val || validate.checkYear(val)) return '';
break;
}
case 'month': {
if (!val || validate.checkYearMonth(val)) return '';
break;
}
case 'date': {
if (!val || validate.checkDate(val)) return '';
break;
}
case 'hourminute': {
if (!val || validate.checkHourMinute(val)) return '';
break;
}
case 'int': { // 整数(字符形式)
if (!val || validate.checkInt(val)) return '';
break;
}
case 'digit': { // 小数(字符形式)
if (!val || validate.checkDigit(val)) return '';
break;
}
default: {
}
}
return val;
},
_setForm: function (idx, val) {
let forms = this.data.forms;
let fields = this.data.fields;
fields[idx].val = val;
forms[idx].val = val;
// TODO是否需要,影响性能
let typeArr = ['text', 'textarea', 'digit', 'idcard', 'int', 'tag'];
// 去掉focus
for (let k = 0; k < fields.length; k++) {
if (helper.isDefined(fields[k].focus)) {
delete fields[k].focus;
}
}
// 提高性能
let formsName = 'forms[' + idx + '].val';
let fieldsName = 'fields[' + idx + '].val';
this.setData({
[formsName]: val,
[fieldsName]: val,
});
//this.triggerEvent('forms', forms);
},
bindImgUploadCmpt: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail;
this._setForm(idx, val);
},
bindLineBlur: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail.value.trim();
this._setForm(idx, val);
},
bindMultiBlur: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail.value;
this._setForm(idx, val);
},
bindDayChange: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail.value.trim();
this._setForm(idx, val);
},
bindAreaChange: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail.value;
this._setForm(idx, val);
},
bindSelectCmpt: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail.trim();
this._setForm(idx, val);
},
bindCheckBoxCmpt: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail;
this._setForm(idx, val);
},
bindRadioCmpt: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail;
this._setForm(idx, val);
},
switchModel: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let val = e.detail.value;
this._setForm(idx, val);
},
bindGetPhoneNumber: async function (e) {
if (e.detail.errMsg == "getPhoneNumber:ok") {
let cloudID = e.detail.cloudID;
let params = {
cloudID
};
let opt = {
title: '手机验证中'
};
await cloudHelper.callCloudSumbit('passport/phone', params, opt).then(res => {
let phone = res.data;
if (!phone || phone.length < 11)
wx.showToast({
title: '手机号码获取失败,请重新绑定手机号码',
icon: 'none',
duration: 2000
});
else {
let idx = pageHelper.dataset(e, 'idx');
this._setForm(idx, phone);
}
});
} else
wx.showToast({
title: '手机号码获取失败,请重新绑定手机号码',
icon: 'none'
});
},
checkForms: function () {
// 写缓存
if (this.data.isCacheMatch) {
cacheHelper.set(this.data.cacheName, this.data.forms, CACHE_FORM_SHOW_TIME);
}
let ret = formSetHelper.checkForm(this.data.fields, this.data.forms, this);
this.setData({
fields: this.data.fields
});
if (!ret) return;
if (this.data.isConfirm) { //是否显示确认信息
this.setData({
showCheckModal: true
});
} else {
cacheHelper.remove(this.data.cacheName);
this.triggerEvent('submit', this.data.forms);
}
},
bindSubmitCmpt: function () {
this.setData({
showCheckModal: false
});
cacheHelper.remove(this.data.cacheName);
this.triggerEvent('submit', this.data.forms);
},
url: function (e) {
pageHelper.url(e, this);
},
getForms: function (isCheckForm = false) {
if (isCheckForm) {
// 是否数据校验
let ret = formSetHelper.checkForm(this.data.fields, this.data.forms, this);
this.setData({
fields: this.data.fields
});
if (!ret) return false;
}
// 写缓存
if (this.data.isCacheMatch) {
cacheHelper.set(this.data.cacheName, this.data.forms, CACHE_FORM_SHOW_TIME);
}
return this.data.forms;
},
getOneFormVal(formName) {
// 取某个表单值
let forms = this.data.forms;
for (let k = 0; k < forms.length; k++) {
if (formName == forms[k].mark) {
return forms[k].val;
}
}
return null;
},
setOneFormVal(formName, val) {
// 设定某个表单值
let forms = this.data.forms;
let fields = this.data.fields;
for (let k = 0; k < forms.length; k++) {
if (formName == forms[k].mark) {
forms[k].val = val;
fields[k].val = val;
break;
}
}
this.setData({
fields,
forms
});
}
},
})
================================================
FILE: miniprogram/cmpts/public/form/form_show/form_show_cmpt.json
================================================
{
"component": true,
"usingComponents": {
"cmpt-checkbox": "/cmpts/public/checkbox/checkbox_cmpt",
"cmpt-radio": "/cmpts/public/radio/radio_cmpt",
"cmpt-img-upload": "/cmpts/public/img/img_upload_cmpt"
}
}
================================================
FILE: miniprogram/cmpts/public/form/form_show/form_show_cmpt.wxml
================================================
function calContent(content) {
var imgCnt = 0;
var textCnt = 0;
for (var k = 0; k < content.length; k++) {
if (content[k].type == 'img') imgCnt++;
if (content[k].type == 'text') textCnt++;
}
if (imgCnt || textCnt) {
return textCnt + '段文字,' + imgCnt + '张图片';
}
else
return '未填写';
}
module.exports = {
calContent: calContent,
};
{{item.title}}:
{{item.val===true?'是':'否'}}
[图文]
{{item.val}}
{{item.title}}
{{item.title}}:
{{item.focus}}
{{item.title}}:
{{item.focus}}
{{item.title}}:
{{item.focus}}
{{item.title}}
{{item.desc}}
{{item.focus}}
*{{item.title}}
至少选择{{item.checkBoxLimit}}项
{{item.focus}}
*{{item.title}}
{{item.focus}}
{{item.title}}
{{item.val || '年,月,日'}}
{{item.desc}}
{{item.focus}}
{{item.title}}
{{item.val || '年份'}}
{{item.desc}}
{{item.focus}}
{{item.title}}
{{item.val || '月份'}}
{{item.desc}}
{{item.focus}}
{{item.title}}
{{item.val || '时间点'}}
{{item.desc}}
{{item.focus}}
{{item.title}}
{{item.val}}
省,区,市
{{item.desc}}
{{item.focus}}
{{item.title}}
{{item.desc}}
{{item.focus}}
*{{item.title}}
{{item.val.length}}/{{item.max}}
{{item.focus}}
*{{item.title}}
多个标签请用逗号分隔
{{item.focus}}
{{item.title}}:
{{item.focus}}
{{item.title}}:{{item.must?'(必填)':''}}
「{{item.title}}」未填写
{{wxs.calContent(item.val)}}
{{item.focus}}
{{item.focus}}
{{item.title}}:
{{item.val||'未填写'}}
{{item.focus}}
{{item.title}}:
{{item.focus}}
{{item.title}}:
{{item.val?'是':'否'}}
{{item.val}}
================================================
FILE: miniprogram/cmpts/public/form/form_show/form_show_cmpt.wxss
================================================
.form-group .picker-base {
flex: 1;
text-align: right;
height: 60rpx;
line-height: 60rpx;
}
.form-group .picker-select {
padding-right: 40rpx;
overflow: hidden;
position: relative;
}
.form-group .phone-text {
font-size: 30rpx;
color: #555;
flex: 1;
text-align: left;
}
.form-group .title {
height: unset !important;
min-height: 60rpx;
}
.form-group-line {
width: 100%;
padding: 1rpx 30rpx;
display: flex;
align-items: center;
min-height: 100rpx;
justify-content: center;
position: relative;
color: #888;
font-size: 28rpx;
margin-top: 10rpx;
}
/* 展示型 */
.form-group-show {
display: flex;
align-items: flex-start;
}
.form-group-show .form-group-show-text {
flex: 1;
padding: 0rpx 0rpx;
text-align: left;
display: flex;
flex-wrap: wrap;
min-height: 60rpx;
align-items: center;
}
.form-group-show .form-group-show-img {
width: 100rpx;
height: 100rpx;
margin-right: 10rpx;
margin-bottom: 10rpx;
border-radius: 5rpx;
}
/* 核对信息窗口 */
.modal-check-info {
width: 100%;
}
.slot-class {
padding: 0 20rpx 30rpx;
}
.modal-check-info .item {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
line-height: 1.8;
font-size: 30rpx;
border-bottom: 1rpx dotted #ddd;
color: #000;
padding: 10rpx 10rpx;
}
.modal-check-info .item:nth-child(odd) {
background-color: #f8f8f8;
}
.modal-check-info .item:last-child {
border-bottom: 0rpx;
}
.modal-check-info .item .title {
font-weight: bold;
max-width: 150rpx;
margin-right: 20rpx;
text-align: left;
}
.modal-check-info .item .content {
flex: 1;
color: #333;
display: flex;
justify-content: flex-start;
align-items: flex-start;
text-align: left;
}
================================================
FILE: miniprogram/cmpts/public/img/img_upload_cmpt.js
================================================
const pageHelper = require('../../../helper/page_helper.js');
const contentCheckHelper = require('../../../helper/content_check_helper.js');
const setting = require('../../../setting/setting.js');
Component({
/**
* 组件的属性列表
*/
properties: {
imgList: {
type: Array,
value: []
},
imgMax: {
type: Number,
value: 4,
},
title: {
type: String,
value: '图片上传',
},
must: { //是否必填
type: Boolean,
value: true,
},
isCheck: { //是否做图片内容校验
type: Boolean,
value: true,
},
isShowNo: { //是否显示序号
type: Boolean,
value: false,
},
imgUploadSize: { //图片最大大小
type: Number,
value: setting.IMG_UPLOAD_SIZE,
},
isShowSize: { //是否提示图片尺寸
type: Boolean,
value: true,
}
},
/**
* 组件的初始数据
*/
data: {
//imgList:[]
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () {
},
ready: function () {
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
/**
* 选择上传图片
*/
bindChooseImgTap: function (e) {
wx.chooseMedia({
count: this.data.imgMax - this.data.imgList.length, //默认9
mediaType: ['image'],
sizeType: ['compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], //从相册选择
success: async (res) => {
wx.showLoading({
title: '图片校验中',
mask: true
});
for (let k = 0; k < res.tempFiles.length; k++) {
let size = res.tempFiles[k].size;
let path = res.tempFiles[k].tempFilePath;
if (!contentCheckHelper.imgTypeCheck(path)) {
wx.hideLoading();
return pageHelper.showNoneToast('只能上传png、jpg、jpeg格式', 3000);
}
let imageMaxSize = 1024 * 1000 * this.data.imgUploadSize;
if (!contentCheckHelper.imgSizeCheck(size, imageMaxSize)) {
wx.hideLoading();
return pageHelper.showNoneToast('单张图片大小不能超过 ' + this.data.imgUploadSize + 'M', 3000);
}
// 读取文件流,云校验
//let imgData = wx.getFileSystemManager().readFileSync(path, 'base64');
//console.log('imgData size=' + imgData.length);
if (this.data.isCheck) {
let check = await contentCheckHelper.imgCheck(path);
if (!check) {
wx.hideLoading();
return pageHelper.showNoneToast('存在不合适的图片, 已屏蔽', 3000);
}
}
this.setData({
imgList: this.data.imgList.concat(path)
});
this.triggerEvent('upload', this.data.imgList);
}
wx.hideLoading();
}
});
},
bindPreviewImgTap: function (e) {
wx.previewImage({
urls: this.data.imgList,
current: e.currentTarget.dataset.url
});
},
/**
* 删除图片
*/
catchDelImgTap: function (e) {
let that = this;
let callback = function () {
that.data.imgList.splice(e.currentTarget.dataset.index, 1);
that.setData({
imgList: that.data.imgList
});
that.triggerEvent('upload', that.data.imgList);
}
pageHelper.showConfirm('确定要删除该图片吗?', callback);
},
}
})
================================================
FILE: miniprogram/cmpts/public/img/img_upload_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/img/img_upload_cmpt.wxml
================================================
*{{title}} (每张不超过{{imgUploadSize}}M)
{{imgList.length}}/{{imgMax}}
#{{index}}
================================================
FILE: miniprogram/cmpts/public/img/img_upload_cmpt.wxss
================================================
@import "../../../style/base/comm.wxss";
@import "../../../style/public/project.wxss";
.form-group .upload-img .img-no {
position: absolute;
right: 0;
bottom: 0;
border-top-left-radius: 6rpx;
padding: 6rpx 12rpx;
height: 35rpx;
opacity: .9;
}
================================================
FILE: miniprogram/cmpts/public/list/comm_list_cmpt.js
================================================
const cloudHelper = require('../../../helper/cloud_helper.js');
const helper = require('../../../helper/helper.js');
const PublicBiz = require('../../../comm/biz/public_biz.js');
const pageHelper = require('../../../helper/page_helper.js');
Component({
options: {
addGlobalClass: true,
pureDataPattern: /^_dataList/, // 指定所有 _ 开头的数据字段为纯数据字段
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
/**
* 组件的属性列表
*/
properties: {
doDate: {
type: Boolean,
value: false
},
listHeight: { // 列表区高度
type: String,
value: ''
},
route: { // 业务路由
type: String,
value: ''
},
source: { // 来源 admin/user
type: String,
value: 'user'
},
_params: { // 路由的附加参数
type: Object,
value: null,
observer: function (newVal, oldVal) { //TODO????
if (!oldVal || !newVal) return; //页面data里赋值会引起触发,除非在组件标签里直接赋值,或者提前赋值
// 清空当前选择
if (newVal) {
this.setData({
pulldownMaskShow: false //返回去遮罩
});
this._fmtSearchData();
}
this.data._dataList = null;
this.triggerEvent('list', { //TODO 考虑改为双向数据绑定model
dataList: this.data._dataList
});
this._getList(1);
}
},
isTotalMenu: {
type: Boolean, //是否整个搜索菜单显示
value: true
},
_items: { // 下拉菜单基础数据
type: Array,
value: [],
observer: function (newVal, oldVal) {
if (newVal) this._fmtSearchData();
}
},
_menus: { // 非下拉菜单基础数据
type: Array,
value: [],
observer: function (newVal, oldVal) {
if (newVal) this._fmtSearchData(); //置为纯数据字段则不触发
}
},
_dataList: {
type: Object,
value: null
},
type: {
type: String, //业务类型 info,user,well
value: ''
},
placeholder: {
type: String,
value: '搜索关键字'
},
sortMenusDefaultIndex: {
type: Number,
value: -1 //横菜单默认选中的
},
search: {
type: String, //搜索框关键字
value: '',
observer: function (newVal, oldVal) {
// 清空当前选择
if (newVal) {
this.setData({
pulldownMaskShow: false //返回去遮罩
});
this._fmtSearchData();
}
this.data._dataList = null;
this.triggerEvent('list', { //TODO 考虑改为双向数据绑定model
dataList: this.data._dataList
});
this._getList(1);
}
},
whereEx: {
type: Object, // 附加查询条件
value: null,
},
returnUrl: {
type: String, // 搜索完返回页面
value: '',
},
topBottom: {
type: String, // 回顶部按钮的位置
value: '50'
},
isCache: { // 非缓存状态下或者list缓存过期下onshow加载, 缓存下onload加载
type: Boolean, //是否cache
value: true
},
pulldownType: {
type: Array, // 下拉菜单展示模式 list/modal 每个菜单一个
value: ['list', 'list', 'list', 'list', 'list', 'list']
},
},
/**
* 组件的初始数据
*/
data: {
refresherTriggered: false, //下拉刷新是否完成
sortItems: [], //下拉
sortMenus: [], //一级菜单非下拉
sortType: '', //回传的类型
sortVal: '', // 回传的值
sortItemIndex: -1,
sortIndex: -1,
topNum: 0, //回顶部
topShow: false,
pulldownMaskShow: false, //下拉菜单遮罩
startDate: '',//默认起始时间
endDate: '',//默认结束时间
},
lifetimes: {
created: function () {
// 组件实例化,但节点树还未导入,因此这时不能用setData
},
attached: function () {
// 在组件实例进入页面节点树时执行
// 节点树完成,可以用setData渲染节点,但无法操作节点
},
ready: async function () {
// 组件布局完成,这时可以获取节点信息,也可以操作节点
this._fmtSearchData();
if (this.data.isCache) //缓存状态下加载
await this._getList(1);
//取得预置参数_params的选中状态
let params = this.data._params;
if (params && params.sortType && helper.isDefined(params.sortVal)) {
let sortMenus = this.data._menus;
for (let k = 0; k < sortMenus.length; k++) {
if (params.sortType == sortMenus[k].type && params.sortVal == sortMenus[k].value) {
this.setData({
//sortType: sortMenus[k].type,
//sortVal: sortMenus[k].value,
sortMenusDefaultIndex: k
});
break;
}
}
}
},
move: function () {
// 组件实例被移动到树的另一个位置
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
pageLifetimes: {
async show() {
// 页面被展示
if (!this.data.isCache || !PublicBiz.isCacheList(this.data.type)) {
// 非缓存状态下或者 list缓存过期下加载
await this._getList(1);
}
},
hide() {
// 页面被隐藏
},
resize(size) {
// 页面尺寸变化
}
},
/**
* 组件的方法列表
*/
methods: {
reload: async function () {
await this._getList(1);
},
// 数据列表
_getList: async function (page) {
let params = {
page: page,
...this.data._params
};
if (this.data.whereEx) params.whereEx = this.data.whereEx;
// 搜索关键字
if (this.data.search)
params.search = this.data.search;
// 搜索菜单
if (this.data.sortType && helper.isDefined(this.data.sortVal)) {
params.sortType = this.data.sortType;
params.sortVal = this.data.sortVal;
}
//if (page == 1 && !this.data._dataList) { TODO???
if (page == 1) {
this.triggerEvent('list', {
dataList: null //第一页面且没有数据提示加载中
});
}
let opt = {};
//if (this.data._dataList && this.data._dataList.list && this.data._dataList.list.length > 0)
opt.title = 'bar';
await cloudHelper.dataList(this, '_dataList', this.data.route, params, opt);
this.triggerEvent('list', { //TODO 考虑改为双向数据绑定model
sortType: this.data.sortType,
dataList: this.data._dataList
});
if (this.data.isCache)
PublicBiz.setCacheList(this.data.type);
if (page == 1) this.bindTopTap();
},
bindReachBottom: function () {
// 上拉触底
this._getList(this.data._dataList.page + 1);
},
bindPullDownRefresh: async function () {
// 下拉刷新
this.setData({
refresherTriggered: true
});
await this._getList(1);
this.setData({
refresherTriggered: false
});
},
/**
* 顶部位置
* @param {*} e
*/
bindScrollTop: function (e) {
if (e.detail.scrollTop > 100) {
this.setData({
topShow: true
});
} else {
this.setData({
topShow: false
});
}
},
/**
* 一键回到顶部
*/
bindTopTap: function () {
this.setData({
topNum: 0
});
},
// 初始化搜索
_fmtSearchData: function () {
let data = {};
let sortItems = [];
let items = this.data._items;
for (let k = 0; k < items.length; k++) {
let item = {
show: false,
items: items[k]
};
sortItems.push(item);
}
data.sortItems = sortItems;
data.sortMenus = this.data._menus;
data.sortItemIndex = -1;
data.sortIndex = -1;
data.sortType = '';
data.sortVal = '';
this.setData(data);
},
/**
* 清除搜索条件
*/
bindSearchClearTap: function () {
// 请求父页面清空搜索
if (this.data.search) {
this.triggerEvent('list', {
search: ''
});
}
},
// 分类&排序一级菜单选择
bindSortTap: function (e) {
let sortIndex = e.currentTarget.dataset.index;
let sortItems = this.data.sortItems;
// 横菜单默认选中取消 TODO
/*
this.setData({
sortMenusDefaultIndex: -1
});*/
// 换了下拉菜单
let sortItemIndex = (sortIndex != this.data.sortIndex) ? -1 : this.data.sortItemIndex;
if (sortIndex < 5) {
let pulldownMaskShow = this.data.pulldownMaskShow;
// 有下拉
for (let i = 0; i < sortItems.length; i++) {
if (i != sortIndex)
sortItems[i].show = false;
else {
sortItems[i].show = !sortItems[i].show;
pulldownMaskShow = sortItems[i].show;
}
}
this.setData({
pulldownMaskShow, //遮罩
sortItems,
sortIndex,
sortItemIndex
});
} else {
//无下拉
for (let i = 0; i < sortItems.length; i++) {
sortItems[i].show = false;
}
this.setData({
pulldownMaskShow: false,
sortItems,
sortIndex,
sortItemIndex
});
this._getSortKey();
}
},
/**
* 下拉菜单选择
*/
bindSortItemTap: function (e) {
let sortItemIndex = e.target.dataset.idx;
if (!sortItemIndex) sortItemIndex = 0; // #46
let sortItems = this.data.sortItems;
for (let i = 0; i < sortItems.length; i++) {
sortItems[i].show = false;
}
this.setData({
pulldownMaskShow: false,
sortItemIndex,
sortItems
});
this._getSortKey();
},
// 获得排序关键字
_getSortKey: function () {
let sortVal = '';
let sortType = '';
let oldSortVal = this.data.sortVal;
let oldSortType = this.data.sortType;
if (this.data.sortIndex < 5) {
sortVal = this.data.sortItems[this.data.sortIndex].items[this.data.sortItemIndex].value;
sortType = this.data.sortItems[this.data.sortIndex].items[this.data.sortItemIndex].type;
} else {
sortVal = this.data.sortMenus[this.data.sortIndex - 5].value;
sortType = this.data.sortMenus[this.data.sortIndex - 5].type;
}
this.setData({
sortVal,
sortType
});
if (sortVal != oldSortVal || sortType != oldSortType) {
// 点击分类
if (this.data.startDate || this.data.endDate) {
this.setData({
startDate: '',
endDate: ''
});
}
if (this.data.search) {
//清空搜索
this.triggerEvent('list', {
search: ''
});
} else
this._getList(1);
}
},
// 搜索跳转
bindSearchTap: function (e) {
wx.navigateTo({
url: pageHelper.fmtURLByPID('/pages/search/search?source=' + this.data.source + '&type=' + this.data.type + '&returnUrl=' + this.data.returnUrl)
});
},
getSortIndex: function () { //获得横向菜单
return this.data.sortIndex;
},
setSortIndex: function (sortIndex) { //设置横向菜单
this.setData({
sortIndex
});
},
bindDateStartChange(e) {
this.setData({
startDate: e.detail.value,
});
},
bindDateEndChange(e) {
this.setData({
endDate: e.detail.value,
});
},
bindDateSearchTap: function (e) {
if (!this.data.startDate.includes('-')) return pageHelper.showNoneToast('请选择开始日期');
if (!this.data.endDate.includes('-')) return pageHelper.showNoneToast('请选择结束日期');
let search = this.data.startDate + '#' + this.data.endDate;
this.setData({
search
})
this._getList(1);
},
bindDateClearTap: function (e) {
this.setData({
startDate: '',
endDate: '',
});
if (this.data.search) {
this.setData({
search: ''
});
}
}
}
})
================================================
FILE: miniprogram/cmpts/public/list/comm_list_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/list/comm_list_cmpt.wxml
================================================
{{startDate||'开始日期'}}
~
{{endDate||'结束日期'}}
搜索
清空
{{ index==sortIndex && item.items[sortItemIndex] ? item.items[sortItemIndex].label: item.items[0].label}}
全部{{its.label}}
取消
确定
================================================
FILE: miniprogram/cmpts/public/list/comm_list_cmpt.wxss
================================================
/*sort*/
.tabs {
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 28rpx;
color: #aaa;
height: 80rpx;
line-height: 80rpx;
}
.tabs .tab {
flex-grow: 1;
text-align: center;
position: relative;
margin-right: 10rpx;
font-size: 28rpx;
color: #000;
}
.tabs .cur {
font-weight: bold;
}
.tabs .tab-menu.cur:after {
content: " ";
position: absolute;
left: 50%;
bottom: 0rpx;
width: 50%;
height: 6rpx;
border-radius: 2rpx;
background-color: orange;
transform: translateX(-50%)
}
.tabs .icon {
color: #000;
}
.sort {
position: absolute;
top: 178rpx;
bottom: 0;
width: 100%;
background-color: rgba(188, 188, 188, 0.3);
z-index: 999;
left: 0
}
.sort .sort-item {
border-top: 1px solid #eee;
height: 80rpx;
line-height: 80rpx;
padding-left: 50rpx;
background-color: #fff;
}
/**头部*/
.top_bar {
width: 100%;
}
.top_bar_scroll {
position: fixed;
top: 0rpx;
left: 0;
z-index: 99;
background: #fff;
z-index: 999;
}
.box-list {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
.box-list-scroll {
flex: 1;
height: 1px;
}
.top-button {
position: fixed;
bottom: 50rpx;
right: 30rpx;
opacity: .8;
}
.list-scroll-view {
display: inline-block;
padding: 0 10rpx;
}
.pulldown-mask {
width: 100%;
height: 100%;
position: fixed;
background-color: #999;
top: 0;
left: 0;
opacity: 0.5;
}
.pulldown-scroll-view {
height: 840rpx;
width: 100%;
}
.pulldown-btn {
font-size: 28rpx;
color: #333 !important;
height: 100rpx;
width: 200rpx;
line-height: 1.3;
padding: 0 15rpx;
}
/* date */
.search-date {
width:100%;
display: flex;
align-items: center;
padding: 0 20rpx;
}
.search-date .date-title {
font-size: 28rpx;
margin-right: 8rpx;
}
.search-date .date-group {
color: #888;
border: 1rpx solid #A4A6AE;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 5rpx 20rpx;
font-size: 29rpx;
}
.search-date .text-line1 {
color: #A4A6AE;
margin: 0 18rpx;
}
.search-date .text-line2 {
color: rgb(0, 0, 0, 0.8);
}
.search-date .search-date-picker {
width: 150rpx;
}
================================================
FILE: miniprogram/cmpts/public/modal/modal_cmpt.js
================================================
// cmpts/public/modal/modal.js
Component({
options: {
addGlobalClass: true,
multipleSlots: true
},
externalClasses: ['slot-class'],
/**
* 组件的属性列表
*/
properties: {
type: { // 类型 comm/bottom/dialog/image
type: String,
value: 'comm'
},
title: {
type: String,
value: '温馨提示'
},
subtitle: {
type: String,
value: ''
},
subtitleAlign: {
type: String,
value: 'center'
},
show: {
type: Boolean,
value: true
},
cancelText: {
type: String,
value: '取消'
},
confirmText: {
type: String,
value: '确定'
},
showConfirm: {
type: Boolean,
value: true
},
imgURL: {
type: String,
value: ''
},
height: {
type: Number,
value: 600
},
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
bindHideModalTap: function (e) {
this.setData({
show: ''
})
},
nomove: function () {},
bindComfirmTap: function (e) {
this.triggerEvent('click', {});
}
}
})
================================================
FILE: miniprogram/cmpts/public/modal/modal_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/modal/modal_cmpt.wxml
================================================
{{title}}
取消
确定
{{title}}
{{subtitle}}
{{cancelText}}
{{confirmText}}
{{title}}
{{subtitle}}
{{cancelText}}
{{confirmText}}
{{title}}
================================================
FILE: miniprogram/cmpts/public/modal/modal_cmpt.wxss
================================================
.bg-img {
width: 100%;
height: 100%;
position: relative;
}
.bg-img .bg-img-image {
top: 0;
left: 0;
position: absolute;
width: 100%;
height: 100%;
}
.bg-img .action .icon-close {
font-size: 50rpx !important;
font-weight: bold;
}
.modal .dialog {
padding: 20rpx 0rpx;
margin-top:30rpx;
}
.modal .dialog .bar {
background-color: #f8f8f8;
}
.modal .dialog .bar .content {
color: #333;
font-size: 36rpx;
}
.modal-bar {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
border-top: 1rpx solid #ccc;
line-height: 3;
}
.modal-bar .modal-bar-view {
width: 50%;
font-size: 36rpx;
font-weight: bold;
}
.modal-bar .modal-bar-comfirm {
color: #576B95;
border-left: 1rpx solid #ccc;
}
.modal .modal-dialog {
padding: 10rpx 0rpx 0;
}
.modal .modal-dialog .subtitle {
width:100%;
text-align: center;
font-size:30rpx;
color:#777;
padding:0 60rpx;
margin-top:10rpx;
margin-bottom:20rpx;
}
================================================
FILE: miniprogram/cmpts/public/picker/picker_cmpt.js
================================================
const helper = require('../../../helper/helper.js');
const dataHelper = require('../../../helper/data_helper.js');
const pageHelper = require('../../../helper/page_helper.js');
Component({
externalClasses: ['outside-picker-class'],
options: {
//addGlobalClass: true
},
/**
* 一维格式: 可以通过model返回
* 对象格式: {label:'对象A',val:'5'}, {label:'对象B',val:'12'}, {label:'对象C',val:'99'}
* 简单形式:['形式1','形式2','形式33']
* 字符串形式
*/
/**
* N维格式: 只能通过trigger返回
* 对象格式: {label:'对象A',val:'5'}, {label:'对象B',val:'12'}, {label:'对象C',val:'99'}
* 简单形式:['形式1','形式2','形式33']
*
*/
properties: {
mark: {
type: String,
value: '',
},
isSlot: { //是否开启slot
type: Boolean,
value: false,
},
sourceData: { //源数组,sourceData有几维,Picker就可以有几阶 简单形式待选项,,,
type: Array,
value: [],
},
sourceDataStr: { //源数组,sourceData有几维,Picker就可以有几阶 简单形式待选项,,,
type: String,
value: '',
},
// key
labelKey: {
type: String,
value: ''
},
// 阶数
steps: {
type: Number,
value: 1
},
noDataHint: { // 无数据的提示语
type: String,
value: '请选择',
},
// 选中项的下标数组 1维
index: {
type: Number,
value: 0
},
// 选中项的下标数组 N维
indexMulti: {
type: Array,
value: []
},
// 默认选中项的值数组 1维
item: {
type: String,
value: '',
observer: function (newVal, oldVal) {
// console.log('one observer', this.data.mark);
if (newVal != oldVal) {
let options = this.data.options;
if (!options || options.length == 0) this._init();
if (options && options.length > 0) this.selected(newVal);
}
}
},
// 默认选中项的值数组 N维
itemMulti: {
type: Array,
value: [],
observer: function (newVal, oldVal) {
// console.log('multi observer', this.data.mark);
if (JSON.stringify(newVal) != JSON.stringify(oldVal)) {
let options = this.data.options;
if (!options || options.length == 0) this._init();
if (options && options.length > 0) this.selected(newVal);
}
}
},
// 是否禁用
disabled: {
type: Boolean,
value: false,
},
disabledHint: { // 禁用提示
type: String,
value: '',
},
},
/**
* 组件的初始数据
*/
data: {
options: null,
idx: 0,
multiDesc: '', // 多选的显示文字
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () { },
ready: function () {
if (!this.data.options || this.data.options.length == 0) this._init();
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
_init: function () {
let sourceData = this.data.sourceData;
let labelKey = this.data.labelKey;
let idx = this.data.idx;
// 字符串形式
if (this.data.steps == 1 &&
this.data.sourceDataStr &&
(!sourceData || sourceData.length == 0)
) {
sourceData = dataHelper.getSelectOptions(this.data.sourceDataStr);
this.setData({
sourceData
});
}
if (!sourceData || sourceData.length == 0) return;
if (this.data.steps == 1) {
if (sourceData.length > 0 && helper.isDefined(sourceData[0]['label'])) {
labelKey = 'label';
}
idx = this.data.index;
} else if (this.data.steps > 1) {
if (sourceData.length > 0 && helper.isDefined(sourceData[0][0]['label'])) {
labelKey = 'label';
}
idx = this.data.indexMulti;
}
this.setData({
idx,
labelKey,
options: sourceData
});
this._getMultiDesc();
if (this.data.steps == 1)
this.selected(this.data.item);
else
this.selected(this.data.itemMulti);
},
_getMultiDesc: function () {
let idx = this.data.idx;
let options = this.data.options;
if (idx.length != options.length) return;
let multiDesc = [];
if (this.data.labelKey) {
for (let k = 0; k < options.length; k++) {
multiDesc[k] = options[k][idx[k]].label;
}
} else {
for (let k = 0; k < options.length; k++) {
multiDesc[k] = options[k][idx[k]];
}
}
this.setData({
multiDesc
});
},
bindTap: function (e) { // 点击行为
if (this.data.disabled && this.data.disabledHint) {
pageHelper.showModal(this.data.disabledHint, '提示', null, '知道了');
}
},
// 触发改变
bindChange: function (e) {
let idx = e.detail.value;
let val = null;
if (this.data.steps == 1) {
val = this.data.labelKey ? this.data.options[idx].val : this.data.options[idx];
this.setData({
item: val,
index: idx
});
} else {
val = [];
let options = this.data.options;
if (this.data.labelKey) {
for (let k = 0; k < options.length; k++) {
val[k] = options[k][idx[k]].val;
}
} else {
for (let k = 0; k < options.length; k++) {
val[k] = options[k][idx[k]];
}
}
this._getMultiDesc();
}
this.triggerEvent('select', val);
},
// 一维数组根据val获取lable
getLabelOneStep: function (val) {
for (let k = 0; k < this.data.sourceData.length; k++) {
if (this.data.sourceData[k].val == val) return this.data.sourceData[k].label;
}
return 'unknown';
},
// 选中值
selected: function (val) {
let options = this.data.options;
let labelKey = this.data.labelKey;
if (this.data.steps == 1) {
for (let k = 0; k < options.length; k++) {
if (labelKey && val == options[k].val) {
this.setData({
idx: k
});
return;
} else if (!labelKey && val == options[k]) {
this.setData({
idx: k
});
return;
}
}
this.setData({
idx: -1
});
//传入数据不匹配的时候,修正父页面传入的的默认值
this.triggerEvent('select', '');
} else if (this.data.steps > 1) {
let idx = [];
for (let k = 0; k < options.length; k++) {
let levelTwo = options[k];
for (let j in levelTwo) {
if (labelKey && val[k] == options[k][j].val) {
idx.push(j);
} else if (!labelKey && val[k] == options[k][j]) {
idx.push(j);
}
}
}
if (idx.length != options.length) idx = [];
this.setData({
idx
});
this._getMultiDesc();
//传入数据不匹配的时候,修正父页面传入的的数组默认值 TODO
}
}
}
})
================================================
FILE: miniprogram/cmpts/public/picker/picker_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/picker/picker_cmpt.wxml
================================================
{{options[idx].label || noDataHint}}
{{options[idx] || noDataHint}}
{{multiDesc || noDataHint}}
================================================
FILE: miniprogram/cmpts/public/picker/picker_cmpt.wxss
================================================
.picker-cmpt {
line-height: 100rpx;
font-size: 28rpx;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width: 100%;
text-align: right;
padding-right: 40rpx;
}
.disabled {
background-color: #eee;
color:#999;
}
.picker-cmpt::before {
position: absolute;
top: 0;
right: 30rpx;
bottom: 0;
display: block;
margin: auto;
width: 30rpx;
height: 30rpx;
color: var(--grey);
content: "\e6a3";
text-align: center;
font-size: 34rpx;
font-family: "icon";
line-height: 30rpx;
margin-top: auto;
margin-right: auto;
margin-bottom: auto;
margin-left: auto;
}
================================================
FILE: miniprogram/cmpts/public/picker_multi/picker_multi_cmpt.js
================================================
/* 参考文档: https://github.com/IceApriler/miniprogram-picker */
/*
[{
label:'ddd' // 展示数据的字段名称
val:'v1',
},
{
label:'cccc',
val:'v2'
}]
*/
const dataHelper = require('../../../helper/data_helper.js');
function isExist(field) {
return field !== null && field !== undefined
}
Component({
externalClasses: ['outside-picker-multi-class'],
/**
* 组件的属性列表
*/
properties: {
// 特定类型 time
mode: { // minute
type: String,
value: ''
},
// time特定类型 对应的分钟步长
timeModeStep: {
type: Number,
value: 1
},
// 初始化时,是否需要自动返回结果给开发者
autoSelect: {
type: Boolean,
value: false
},
// 源数组,sourceData有几维,Picker就可以有几阶
sourceData: {
type: Array,
value: [],
observer: 'sourceDataChange'
},
// 阶数
steps: {
type: Number,
value: 1
},
// 选择了第n列后,是否将大于n的列的选择值自动初始化为0
initColumnSelectedIndex: {
type: Boolean,
value: false,
},
// 默认选中项的下标数组
itemIndex: {
type: Array,
value: []
},
// 默认选中项的值数组
itemMulti: {
type: Array,
value: [],
observer: function (newVal, oldVal) {
if (JSON.stringify(newVal) != JSON.stringify(oldVal)) {
this.sourceDataChange(this.data.sourceData);
}
}
},
// 是否禁用
disabled: {
type: Boolean,
value: false,
},
isSlot: { //是否开启slot
type: Boolean,
value: true,
},
},
/**
* 组件的初始数据
*/
data: {
// Picker当前所选择的索引数组 => 比如:[0, 0, 2],表示第一列选择第0项,第二列选择第0项,第三列选择第2项。
multiIndex: {
type: Array,
value: [],
},
// Picker当前所展示的数组 => 比如:[['河北', '山东'], ['石家庄', '保定'], ['桥西区', '裕华区', '长安区']]。
multiArray: {
type: Array,
value: [],
},
// 用户点击确定后,所选择的值数组 => 比如:
// [{name: '河北', id: '3110'}, {name: '石家庄', id: '3110xx'}, {name: '长安区', id: '3110xxx'}]。
selectedArray: {
type: Array,
value: [],
},
},
/**
* 生命周期方法
*/
lifetimes: {
created: function () {},
attached: function () {
if (this.data.autoSelect) {
this.processData();
}
},
ready: function () {
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
pageLifetimes: {
show: function () {
},
hide: function () {
// 页面被隐藏
},
resize: function (size) {
// 页面尺寸变化
}
},
/**
* 组件的方法列表
*/
methods: {
/**
* 监听源数组更新变化
*
* @param {Array} newSourceData 源数组,newSourceData有几维,Picker就可以有几阶。
*/
sourceDataChange: function (newSourceData) {
const {
steps
} = this.data;
// 源数组更新,则需要更新multiIndex、multiArray
const multiIndex = [];
const multiArray = [];
newSourceData = this.checkSourceData(newSourceData);
// console.warn(newSourceData)
const itemIndex = this.getDefaultIndex(newSourceData);
const handle = (source = [], columnIndex = 0) => {
// 当前遍历Picker的第columnIndex列,
// 当columnIndex = 0时,source表示sourceData,其它则表示子集subset
const _multiArrayColumn0 = [];
source.forEach((item, index) => {
if (columnIndex === 0) {
// newSourceData的第0维要单独处理,最后unshift到multiArray中
_multiArrayColumn0.push(item.label)
}
if (isExist(item.label) && index === (itemIndex[columnIndex] || 0)) {
// 选中的索引和值,默认取每列的第0个
multiIndex.push(index);
if (columnIndex < steps - 1) {
if (isExist(item.children)) {
// 开始处理下一维的数据
const _subsetArr = item.children.map(sub => sub.label);
multiArray.push(_subsetArr);
handle(item.children, columnIndex + 1);
}
}
}
})
if (columnIndex === 0) {
multiArray.unshift(_multiArrayColumn0);
}
}
handle(newSourceData);
this.setData({
multiIndex,
multiArray
})
if (this.data.autoSelect) {
this.processData();
}
},
/**
* 获取默认值index
* @param {Array} newSourceData 源数组
*/
getDefaultIndex: function (newSourceData) {
const {
itemIndex,
itemMulti,
steps,
} = this.data;
if (itemIndex.length) {
return itemIndex; // 返回默认选中的下标数据
} else if (itemMulti.length) {
if (itemMulti.length !== steps) {
this.consoleError(new Error('你设置的"itemMulti"字段阶数与"steps"不符,请修改后再试。'));
return [];
} else {
const _defaultIndex = [];
const handle = (source = [], columnIndex = 0) => {
// 默认值
_defaultIndex[columnIndex] = 0;
source.forEach((item, index) => {
if (
(itemMulti[columnIndex]) ===
(item.val)
) {
_defaultIndex[columnIndex] = index;
if (columnIndex < steps - 1) {
if (item.children) {
// 开始处理下一维的数据
handle(item.children, columnIndex + 1);
}
}
}
})
}
handle(newSourceData);
return _defaultIndex;
}
} else {
return [];
}
},
/**
* 校验源数组是否正确
*
* @param {Array} sourceData 源数组
*/
checkSourceData: function (sourceData) {
const {
steps
} = this.data;
const handle = (source = [], columnIndex = 0) => {
// 当前遍历Picker的第columnIndex列,
// 当columnIndex = 0时,source表示sourceData,其它则表示子集subset
if (!source.length) {
const temp = {};
temp.label = '';
temp.children = [];
source.push(temp);
}
return source.map((item) => {
// 有label字段才会去遍历children字段
if (columnIndex < steps - 1) {
// 开始处理下一维的数据
item.children = handle(item.children, columnIndex + 1);
}
return item;
})
}
return handle(sourceData);
},
/**
* 用户点击了确认。
*
* @param {Object} e 事件对象,具体参考微信小程序api。
* @param {Array} e.detail.value 用户选择的下标数组。
*/
pickerChange: function (e) {
// console.log('picker发送选择改变,携带值为', e.detail.value)
this.setData({
multiIndex: e.detail.value
})
this.processData();
},
/**
* 处理最终数据,将返回给开发者。
*
*/
processData: function () {
const {
sourceData,
multiIndex
} = this.data;
let selectedArray = [];
const handle = (source = [], columnIndex = 0) => {
source.forEach((item, index) => {
if (index === multiIndex[columnIndex]) {
let node = dataHelper.deepClone(item);
delete node.children;
selectedArray.push(node);
if (columnIndex < this.data.steps - 1) {
handle(item.children, columnIndex + 1);
}
}
})
}
handle(sourceData);
this.setData({
selectedArray
});
/*
const detail = {
selectedIndex: this.data.multiIndex,
selectedArray: this.data.selectedArray
}*/
let ret = dataHelper.getArrByKey(selectedArray, 'val');
this.triggerEvent('select', ret);
},
/**
* 用户滚动了某一列。
*
* @param {Object} e 事件对象,具体参考微信小程序api。
*/
pickerColumnChange: function (e) {
const {
multiArray,
sourceData,
steps,
initColumnSelectedIndex
} = this.data;
let {
multiIndex
} = this.data;
const {
column,
value: changeIndex
} = e.detail;
// console.log(`修改了Picker的第${column}列(从0开始计算),选中了第${changeIndex}个值(从0开始计算)`)
// multiIndex变化了,所以也要同步更新multiArray
multiIndex[column] = changeIndex;
if (initColumnSelectedIndex) {
// 每次重置之后的index为0,但是有bug,待定。 => 经检查,是编辑器的问题,真机上是没有问题的。
const _multiIndex = multiIndex.map((item, index) => {
if (column >= index) {
return item;
} else {
return 0;
}
})
multiIndex = _multiIndex;
}
const handle = (source = [], columnIndex = 0) => {
// 当前遍历第 columnIndex 列
source.forEach((item, index) => {
if (index === multiIndex[columnIndex]) {
if (columnIndex < steps - 1) {
if (!item.children) {
item.children = [];
}
const multiArrayItem = item.children.map((sub) => sub.label);
// 从第1列开始,才有可能变化
multiArray[columnIndex + 1] = multiArrayItem;
handle(item.children, columnIndex + 1);
}
}
})
}
handle(sourceData);
this.setData({
multiArray,
multiIndex,
})
this.triggerEvent('columnchange', e);
},
/**
* 用户点击了取消触发
* @param {Object} e 事件对象
*/
pickerCancel: function (e) {
this.triggerEvent('cancel', e);
},
/**
* 绑定console.error
* @param {...any} arg 打印参数
*/
consoleError: function (...arg) {
console.error(...arg);
},
},
})
================================================
FILE: miniprogram/cmpts/public/picker_multi/picker_multi_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/picker_multi/picker_multi_cmpt.wxml
================================================
{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}}
{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}}
{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}},{{multiArray[3][multiIndex[3]]}}
{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}},{{multiArray[3][multiIndex[3]]}},{{multiArray[4][multiIndex[4]]}}
================================================
FILE: miniprogram/cmpts/public/picker_multi/picker_multi_cmpt.wxss
================================================
.picker-cmpt {
line-height: 100rpx;
font-size: 28rpx;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width: 100%;
text-align: right;
}
================================================
FILE: miniprogram/cmpts/public/picker_time/datetime_picker.js
================================================
const timeHelper = require('../../../helper/time_helper.js');
function withData(param, unit = '') {
if (unit) return param;
return param < 10 ? '0' + param : '' + param;
}
function getLoopArray(start, end, unit = '', step = 1) {
start = start || 0;
end = end || 1;
start = parseInt(start);
end = parseInt(end);
let array = [];
let i = 0;
for (i = start; i <= end;) {
array.push(withData(i, unit) + unit);
i += step;
}
if (step > 1 && i != 59) {
array.push(withData(59, unit) + unit);
}
return array;
}
function getMonthDay(year, month, unit = '') {
let flag = year % 400 == 0 || (year % 4 == 0 && year % 100 != 0),
array = null;
month = withData(parseInt(month));
switch (month) {
case '01':
case '03':
case '05':
case '07':
case '08':
case '10':
case '12':
array = getLoopArray(1, 31, unit);
break;
case '04':
case '06':
case '09':
case '11':
array = getLoopArray(1, 30, unit);
break;
case '02':
array = flag ? getLoopArray(1, 29, unit) : getLoopArray(1, 28, unit);
break;
default:
array = '月份格式不正确,请重新输入!'
}
return array;
}
function getNewDateArry() {
// 当前时间的处理
let newDate = new Date();
let year = withData(newDate.getFullYear()),
mont = withData(newDate.getMonth() + 1),
date = withData(newDate.getDate()),
hour = withData(newDate.getHours()),
minu = withData(newDate.getMinutes()),
seco = withData(newDate.getSeconds());
return [year, mont, date, hour, minu, seco];
}
function dateTimePicker(startYear, endYear, date, minuStep = 1) {
// 返回默认显示的数组和联动数组的声明
let dateTimeIndex = [],
dateTimeArray = [
[],
[],
[],
[],
[],
[]
];
let dateTimeArrayPure = [
[],
[],
[],
[],
[],
[]
];
let start = startYear || 1978;
let end = endYear || 2100;
if (date && date.length == 4) date += '-01-01 00:00:00';
if (date && date.length == 7) date += '-01 00:00:00';
if (date && date.length == 10) date += ' 00:00:00';
if (date && date.length == 13) date += ':00:00';
if (date && date.length == 16) date += ':00';
// 默认开始显示数据
let defaultDate = date ? [...date.split(' ')[0].split('-'), ...date.split(' ')[1].split(':')] : getNewDateArry();
// 处理联动列表数据
/*年月日 时分秒*/
dateTimeArray[0] = getLoopArray(start, end, '年');
dateTimeArray[1] = getLoopArray(1, 12, '月');
dateTimeArray[2] = getMonthDay(defaultDate[0], defaultDate[1], '日');
dateTimeArray[3] = getLoopArray(0, 23, '点');
dateTimeArray[4] = getLoopArray(0, 59, '分', minuStep);
dateTimeArray[5] = getLoopArray(0, 59, '秒');
dateTimeArrayPure[0] = getLoopArray(start, end);
dateTimeArrayPure[1] = getLoopArray(1, 12);
dateTimeArrayPure[2] = getMonthDay(defaultDate[0], defaultDate[1]);
dateTimeArrayPure[3] = getLoopArray(0, 23);
dateTimeArrayPure[4] = getLoopArray(0, 59, '', minuStep);
dateTimeArrayPure[5] = getLoopArray(0, 59);
dateTimeArrayPure.forEach((current, index) => {
let idx = current.indexOf(defaultDate[index]);
if (idx < 0) idx = 0;
dateTimeIndex.push(idx);
});
return {
dateTimeArray,
dateTimeIndex
}
}
module.exports = {
dateTimePicker,
getMonthDay
}
================================================
FILE: miniprogram/cmpts/public/picker_time/picker_time_cmpt.js
================================================
const timeHelper = require('../../../helper/time_helper.js');
const dateTimePicker = require('./datetime_picker.js');
Component({
externalClasses: ['picker-class'],
/**
* 组件的属性列表
*/
properties: {
mark: {
type: String,
value: ''
},
// 特定类型 time minute=单纯时间分钟选择
mode: { // year/month/day/hour/minute/fullminute/second
type: String,
value: 'second'
},
// time特定类型 对应的分钟步长
timeModeStep: {
type: Number,
value: 5
},
startYear: {
type: Number,
value: 0
},
endYear: {
type: Number,
value: 2030
},
item: {
type: String,
value: '',
observer: function (newVal, oldVal) {
if (newVal != oldVal) {
this._init();
}
}
},
},
/**
* 组件的初始数据
*/
data: {
multiIndex: [], // Picker当前所选择的索引数组
multiArray: [], // Picker当前所展示的数组
},
lifetimes: {
created: function () { },
attached: function () { },
ready: function () {
// 当前年份
if (this.data.startYear == 0) this.data.startYear = timeHelper.time('Y');
// 单纯分钟选择
if (this.data.mode == 'minute') {
this.data.startYear = 2021;
this.data.endYear = 2021;
if (this.data.item) {
this.data.item = '2021-01-01 ' + this.data.item;
}
}
this._init();
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
_fmtTime: function (str) {
str = str.replace(/[^0-9]/ig, '');
str = parseInt(str);
return str < 10 ? '0' + str : '' + str;
},
// 根据选择获取时间字符串
_getTimeStr: function (selIdex) {
let arr = [];
let multiArray = this.data.multiArray;
for (let k = 0; k < selIdex.length; k++) {
let str = this._fmtTime(multiArray[k][selIdex[k]]);
arr.push(str);
}
let mode = this.data.mode;
switch (mode) {
case 'year':
arr = arr[0];
break;
case 'month':
arr = arr[0] + '-' + arr[1];
break;
case 'day':
arr = arr[0] + '-' + arr[1] + '-' + arr[2];
break;
case 'hour':
arr = arr[0] + '-' + arr[1] + '-' + arr[2] + ' ' + arr[3] + ':00';
break;
case 'fullminute':
arr = arr[0] + '-' + arr[1] + '-' + arr[2] + ' ' + arr[3] + ':' + arr[4];
break;
case 'minute':
arr = arr[0] + ':' + arr[1];
break;
case 'second':
arr = arr[0] + '-' + arr[1] + '-' + arr[2] + ' ' + arr[3] + ':' + arr[4] + ':' + arr[5];
break;
}
return arr;
},
_init: function () {
let multiIndex = [];
let multiArray = [];
let mode = this.data.mode;
let obj = dateTimePicker.dateTimePicker(this.data.startYear, this.data.endYear, this.data.item, this.data.timeModeStep);
let idx = obj.dateTimeIndex;
let arr = obj.dateTimeArray;
switch (mode) {
case 'year':
multiIndex = [idx[0]];
multiArray = [arr[0]];
break;
case 'month':
multiIndex = [idx[0], idx[1]];
multiArray = [arr[0], arr[1]];
break;
case 'day':
multiIndex = [idx[0], idx[1], idx[2]];
multiArray = [arr[0], arr[1], arr[2]];
break;
case 'hour':
multiIndex = [idx[0], idx[1], idx[2], idx[3]];
multiArray = [arr[0], arr[1], arr[2], arr[3]];
break;
case 'fullminute':
idx.pop();
arr.pop();
multiIndex = idx;
multiArray = arr;
break;
case 'minute':
multiIndex = [idx[3], idx[4]];
multiArray = [arr[3], arr[4]];
break;
case 'second':
multiIndex = idx;
multiArray = arr;
break;
}
this.setData({
multiIndex,
multiArray
});
},
pickerCancel: function (e) {
},
// 用户点击了确认
pickerChange: function (e) {
let time = this._getTimeStr(e.detail.value);
this.triggerEvent('select', time);
},
// 用户点击了列选择
pickerColumnChange: function (e) {
let multiArray = this.data.multiArray;
let multiIndex = this.data.multiIndex;
multiIndex[e.detail.column] = e.detail.value;
let mode = this.data.mode;
if (mode != 'year' && mode != 'month' && mode != 'minute') {
let year = (multiArray[0][multiIndex[0]]).replace('年', '');
let month = (multiArray[1][multiIndex[1]]).replace('月', '');
multiArray[2] = dateTimePicker.getMonthDay(year, month, '日');
}
this.setData({
multiArray,
multiIndex
});
}
}
})
================================================
FILE: miniprogram/cmpts/public/picker_time/picker_time_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/picker_time/picker_time_cmpt.wxml
================================================
================================================
FILE: miniprogram/cmpts/public/picker_time/picker_time_cmpt.wxss
================================================
/* cmpts/public/picker_time/picker_time_cmpt.wxss */
================================================
FILE: miniprogram/cmpts/public/poster/poster_cmpt.js
================================================
/*
https://github.com/jasondu/wxa-plugin-canvas
### 标准尺寸:
width: 375, // rpx
height: 670,
### 父页面分享按钮取值
onShareAppMessage: function (e) {
let img = e.target.dataset.img;
return {
title: 'xxx',
imageUrl: img,
path: 'xxxx',
}
}
*/
import Poster from '../../../cmpts/public/poster/wxa-plugin-canvas/poster/poster.js'
const pageHelper = require('../../../helper/page_helper.js');
const picHelper = require('../../../helper/pic_helper.js');
const helper = require('../../../helper/helper.js');
Component({
externalClasses: ['poster-class'],
options: {
addGlobalClass: true,
multipleSlots: true
},
/**
* 组件的属性列表
*/
properties: {
config: { // 图形参数
type: Object,
value: null,
},
isQr: { // 是否叠加小程序码
type: Boolean,
value: false
},
isFace: { // 是否叠加头像
type: Boolean,
value: false
},
doPoster: {
type: Boolean,
value: true
},
show: { // 显示
type: Boolean,
value: false
},
img: { //图片文件
type: String,
value: ''
}
},
/**
* 组件的初始数据
*/
data: {
isLoad: false,
},
lifetimes: {
attached: function () {
},
ready: function () {
this._init();
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
_init: async function () {
},
bindPosterTap: function (e) {
this.setData({
isCreate:true,
isLoad: false,
}, async () => {
await this.createPoster();
});
},
bindCloseTap: function () {
this.setData({
show: false
});
},
/**
* 异步生成海报
*/
createPoster: async function () {
// TODO:根据屏幕大小来生成,但是没有负定位
let posterConfig = {
width: 480, // rpx
height: 650,
pixelRatio: 2, // 2 为原始大小
backgroundColor: '#345678',
debug: false,
}
let config = this.data.config;
if (!helper.isDefined(config['width']))
config.width = posterConfig.width;
if (!helper.isDefined(config['height']))
config.height = posterConfig.height;
if (!helper.isDefined(config['pixelRatio']))
config.pixelRatio = posterConfig.pixelRatio;
if (!helper.isDefined(config['backgroundColor']))
config.backgroundColor = posterConfig.backgroundColor;
if (!helper.isDefined(config['debug']))
config.debug = posterConfig.debug;
//Object.assign(posterConfig, this.data.config); // TODO有问题
this.setData({
posterConfig: config
}, async () => {
await Poster.create(true, this);
});
},
onPosterFail: function (e) {
console.log(e)
},
bindPosterSuccessListener(e) {
let img = e.detail;
this.setData({
img,
isLoad: true
});
},
url: function (e) {
pageHelper.url(e, this);
},
bindPosterFailListener(e) {
console.log(e);
},
bindSaveTap: function (e) {
let that = this;
let callback = function () {
wx.saveImageToPhotosAlbum({
filePath: that.data.img,
success: function (data) {
wx.showToast({
title: '保存成功',
icon: 'success',
duration: 1000
})
},
});
}
picHelper.getWritePhotosAlbum(callback);
}
}
})
================================================
FILE: miniprogram/cmpts/public/poster/poster_cmpt.json
================================================
{
"component": true,
"usingComponents": {
"poster": "./wxa-plugin-canvas/poster"
}
}
================================================
FILE: miniprogram/cmpts/public/poster/poster_cmpt.wxml
================================================
长按图片保存或者转发
生成海报
关闭
================================================
FILE: miniprogram/cmpts/public/poster/poster_cmpt.wxss
================================================
.main-poster .poster-share {
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
padding: 30rpx 50rpx 10rpx;
}
.main-poster .poster-share .item {
flex: 1 0 0;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
color: #333;
}
.main-poster .poster-share .item .pic {
height: 100rpx;
width: 100rpx;
background-color: #f2f2f2;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.main-poster .poster-share .item-share {
margin-top: 15rpx;
}
.main-poster .poster-share .item-share>text {
margin-top: 15rpx !important;
}
.main-poster .poster-share .item .pic image {
height: 50rpx;
width: 50rpx;
}
.main-poster .poster-share .item .pic .friend {
height: 60rpx;
width: 60rpx;
}
.main-poster .poster-share .item>text {
margin-top: 15rpx;
font-size: 30rpx;
text-align: center;
}
.main-poster .poster-img {
display: flex;
justify-content: center;
align-items: center;
padding-top: 0rpx;
padding-bottom: 0rpx;
min-height: 600rpx;
margin-top: 30rpx;
flex-direction: column;
}
.main-poster .poster-img .save-hint {
font-size: 30rpx;
color: #555;
margin-top: 10rpx;
}
.load.loading::after {
content: "海报生成中...";
}
.main-poster .poster-img image {
width: 375rpx;
border-radius: 15rpx;
}
.main-poster .line {
width: 100%;
margin: 30rpx 0 30rpx;
border-top: 1rpx solid #ddd;
padding: 30rpx 0 20rpx;
font-size: 32rpx;
}
================================================
FILE: miniprogram/cmpts/public/poster/poster_cmpt_helper.js
================================================
const cloudHelper = require('../../../helper/cloud_helper.js');
async function config1({
cover,
title,
desc,
qr,
bg = ''
}) {
if (cover.startsWith('cloud'))
cover = await cloudHelper.getTempFileURLOne(cover);
if (qr.startsWith('cloud'))
qr = await cloudHelper.getTempFileURLOne(qr);
let posterConfig = {
width: 480, // rpx
height: 650,
backgroundColor: '#eeeeee'
};
if (bg) posterConfig.backgroundColor = bg;
let blocks = [];
blocks = [{
x: 30,
y: 30,
backgroundColor: '#ffffff',
width: 420,
height: 590,
borderRadius: 20
}];
let texts = [];
texts = [{
x: 50,
y: 350,
text: title,
width: 360,
lineNum: 2,
lineHeight: 40,
fontSize: 26,
color: '#000000',
textAlign: 'left',
zIndex: 9999
},
{
x: 55,
y: 510,
text: '长按识别小程序码',
fontSize: 18,
color: '#aaaaaa',
zIndex: 9999
}, {
x: 55,
y: 540,
text: desc,
fontSize: 18,
color: '#aaaaaa',
zIndex: 9999
}];
let images = [];
if (cover) {
images.push({ // 底图
x: 40,
y: 40,
url: cover,
width: 400,
height: 260,
zIndex: 999
});
}
if (qr) {
images.push({ // 小程序码
x: 310,
y: 460,
url: qr,
width: 120,
height: 120
});
}
posterConfig.texts = texts;
posterConfig.blocks = blocks
posterConfig.images = images;
return posterConfig;
}
module.exports = {
config1
}
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/index/index.js
================================================
const main = {
/**
* 渲染块
* @param {Object} params
*/
drawBlock({
text,
width = 0,
height,
x,
y,
paddingLeft = 0,
paddingRight = 0,
borderWidth,
backgroundColor,
borderColor,
borderRadius = 0,
opacity = 1,
}) {
// 判断是否块内有文字
let blockWidth = 0; // 块的宽度
let textX = 0;
let textY = 0;
if (typeof text !== "undefined") {
// 如果有文字并且块的宽度小于文字宽度,块的宽度为 文字的宽度 + 内边距
const textWidth = this._getTextWidth(typeof text.text === "string" ? text : text.text);
blockWidth = textWidth > width ? textWidth : width;
blockWidth += paddingLeft + paddingLeft;
const {
textAlign = "left", text: textCon
} = text;
textY = height / 2 + y; // 文字的y轴坐标在块中线
if (textAlign === "left") {
// 如果是右对齐,那x轴在块的最左边
textX = x + paddingLeft;
} else if (textAlign === "center") {
textX = blockWidth / 2 + x;
} else {
textX = x + blockWidth - paddingRight;
}
} else {
blockWidth = width;
}
if (backgroundColor) {
// 画面
this.ctx.save();
this.ctx.globalAlpha = opacity;
this.ctx.fillStyle = backgroundColor;
if (borderRadius > 0) {
// 画圆角矩形
this._drawRadiusRect(x, y, blockWidth, height, borderRadius);
this.ctx.fill();
} else {
this.ctx.fillRect(this.toPx(x), this.toPx(y), this.toPx(blockWidth), this.toPx(height));
}
this.ctx.restore();
}
if (borderWidth) {
// 画线
this.ctx.save();
this.ctx.globalAlpha = opacity;
this.ctx.strokeStyle = borderColor;
this.ctx.lineWidth = this.toPx(borderWidth);
if (borderRadius > 0) {
// 画圆角矩形边框
this._drawRadiusRect(x, y, blockWidth, height, borderRadius);
this.ctx.stroke();
} else {
this.ctx.strokeRect(this.toPx(x), this.toPx(y), this.toPx(blockWidth), this.toPx(height));
}
this.ctx.restore();
}
if (text) {
this.drawText(Object.assign(text, {
x: textX,
y: textY
}));
}
},
/**
* 渲染文字
* @param {Object} params
*/
drawText(params) {
const {
x,
y,
fontSize,
color,
baseLine,
textAlign,
text,
opacity = 1,
width,
lineNum,
lineHeight
} = params;
if (Object.prototype.toString.call(text) === "[object Array]") {
let preText = {
x,
y,
baseLine
};
text.forEach((item) => {
preText.x += this.toPx(item.marginLeft || 0);
const textWidth = this._drawSingleText(
Object.assign(item, {
...preText,
})
);
preText.x += textWidth + this.toPx(item.marginRight || 0); // 下一段字的x轴为上一段字x + 上一段字宽度
});
} else {
this._drawSingleText(params);
}
},
/**
* 渲染图片
* @param {Object} params
*/
drawImage({
imgPath,
x,
y,
w,
h,
sx,
sy,
sw,
sh,
borderRadius = 0,
borderWidth = 0,
borderColor
}) {
return new Promise((resolve) => {
const img = this.node.createImage();
img.onload = () => {
this.ctx.save();
if (borderRadius > 0) {
this._drawRadiusRect(x, y, w, h, borderRadius);
this.ctx.strokeStyle = "rgba(255,255,255,0)";
this.ctx.stroke();
this.ctx.clip();
this.ctx.drawImage(
img,
this.toPx(sx),
this.toPx(sy),
this.toPx(sw),
this.toPx(sh),
this.toPx(x),
this.toPx(y),
this.toPx(w),
this.toPx(h)
);
if (borderWidth > 0) {
this.ctx.strokeStyle = borderColor;
this.ctx.lineWidth = this.toPx(borderWidth);
this.ctx.stroke();
}
} else {
this.ctx.drawImage(
img,
this.toPx(sx),
this.toPx(sy),
this.toPx(sw),
this.toPx(sh),
this.toPx(x),
this.toPx(y),
this.toPx(w),
this.toPx(h)
);
}
this.ctx.restore();
resolve();
};
img.src = imgPath;
});
},
/**
* 渲染线
* @param {Object} param
*/
drawLine({
startX,
startY,
endX,
endY,
color,
width
}) {
this.ctx.save();
this.ctx.beginPath();
this.ctx.strokeStyle = color;
this.ctx.lineWidth = this.toPx(width);
this.ctx.moveTo(this.toPx(startX), this.toPx(startY));
this.ctx.lineTo(this.toPx(endX), this.toPx(endY));
this.ctx.stroke();
this.ctx.closePath();
this.ctx.restore();
},
downloadResource({
images = [],
pixelRatio
}) {
// 本方法比create早执行,所以要预先设定ratio by cc 2021/10/25
this.pixelRatio = pixelRatio || this.pixelRatio;
const drawList = [];
images.forEach((image, index) => drawList.push(this._downloadImageAndInfo(image, index)));
return Promise.all(drawList);
},
initCanvas(w, h, debug) {
const {
platform
} = wx.getSystemInfoSync();
return new Promise((resolve) => {
if (platform === "ios") {
this.setData({
pxWidth: this.toPx(w),
pxHeight: this.toPx(h),
debug,
});
// ios系统动态设置canvas宽高后立即绘制canvas会偶现变形的BUG
setTimeout(() => {
resolve();
}, 100);
} else {
this.setData({
pxWidth: this.toPx(w),
pxHeight: this.toPx(h),
debug,
},
resolve
);
}
});
},
};
const handle = {
/**
* 画圆角矩形
*/
_drawRadiusRect(x, y, w, h, r) {
const br = r / 2;
this.ctx.beginPath();
this.ctx.moveTo(this.toPx(x + br), this.toPx(y)); // 移动到左上角的点
this.ctx.lineTo(this.toPx(x + w - br), this.toPx(y));
this.ctx.arc(
this.toPx(x + w - br),
this.toPx(y + br),
this.toPx(br),
2 * Math.PI * (3 / 4),
2 * Math.PI * (4 / 4)
);
this.ctx.lineTo(this.toPx(x + w), this.toPx(y + h - br));
this.ctx.arc(this.toPx(x + w - br), this.toPx(y + h - br), this.toPx(br), 0, 2 * Math.PI * (1 / 4));
this.ctx.lineTo(this.toPx(x + br), this.toPx(y + h));
this.ctx.arc(
this.toPx(x + br),
this.toPx(y + h - br),
this.toPx(br),
2 * Math.PI * (1 / 4),
2 * Math.PI * (2 / 4)
);
this.ctx.lineTo(this.toPx(x), this.toPx(y + br));
this.ctx.arc(this.toPx(x + br), this.toPx(y + br), this.toPx(br), 2 * Math.PI * (2 / 4), 2 * Math.PI * (3 / 4));
},
/**
* 计算文本长度
* @param {Array|Object}} text 数组 或者 对象
*/
_getTextWidth(text) {
let texts = [];
if (Object.prototype.toString.call(text) === "[object Object]") {
texts.push(text);
} else {
texts = text;
}
let width = 0;
texts.forEach(({
fontSize,
text,
marginLeft = 0,
marginRight = 0
}) => {
this.ctx.fontSize = this.toPx(fontSize);
width += this.ctx.measureText(text).width + marginLeft + marginRight;
});
return this.toRpx(width);
},
/**
* 渲染一段文字
*/
_drawSingleText({
x,
y,
fontSize,
color,
baseLine,
textAlign = "left",
text,
opacity = 1,
textDecoration = "none",
width,
lineNum = 1,
lineHeight = 0,
fontWeight = "normal",
fontStyle = "normal",
fontFamily = "sans-serif",
}) {
this.ctx.save();
this.ctx.beginPath();
this.ctx.font = fontStyle + " " + fontWeight + " " + this.toPx(fontSize, true) + "px " + fontFamily;
this.ctx.globalAlpha = opacity;
this.ctx.fillStyle = color;
this.ctx.textBaseline = baseLine;
this.ctx.textAlign = textAlign;
let textWidth = this.toRpx(this.ctx.measureText(text).width);
const textArr = [];
if (textWidth > width) {
// 文本宽度 大于 渲染宽度
let fillText = "";
let line = 1;
for (let i = 0; i <= text.length - 1; i++) {
// 将文字转为数组,一行文字一个元素
fillText = fillText + text[i];
if (this.toRpx(this.ctx.measureText(fillText).width) >= width) {
if (line === lineNum) {
if (i !== text.length - 1) {
fillText = fillText.substring(0, fillText.length - 1) + "...";
}
}
if (line <= lineNum) {
textArr.push(fillText);
}
fillText = "";
line++;
} else {
if (line <= lineNum) {
if (i === text.length - 1) {
textArr.push(fillText);
}
}
}
}
textWidth = width;
} else {
textArr.push(text);
}
textArr.forEach((item, index) => {
this.ctx.fillText(item, this.toPx(x), this.toPx(y + (lineHeight || fontSize) * index));
});
this.ctx.restore();
// textDecoration
if (textDecoration !== "none") {
let lineY = y;
if (textDecoration === "line-through") {
// 目前只支持贯穿线
lineY = y;
// 小程序画布baseLine偏移阈值
let threshold = 5;
// 根据baseLine的不同对贯穿线的Y坐标做相应调整
switch (baseLine) {
case "top":
lineY += fontSize / 2 + threshold;
break;
case "middle":
break;
case "bottom":
lineY -= fontSize / 2 + threshold;
break;
default:
lineY -= fontSize / 2 - threshold;
break;
}
}
this.ctx.save();
this.ctx.moveTo(this.toPx(x), this.toPx(lineY));
this.ctx.lineTo(this.toPx(x) + this.toPx(textWidth), this.toPx(lineY));
this.ctx.strokeStyle = color;
this.ctx.stroke();
this.ctx.restore();
}
return textWidth;
},
};
const helper = {
/**
* 下载图片并获取图片信息
*/
_downloadImageAndInfo(image, index) {
return new Promise((resolve, reject) => {
const {
x,
y,
url,
zIndex
} = image;
const imageUrl = url;
// 下载图片
this._downImage(imageUrl, index)
// 获取图片信息
.then((imgPath) => this._getImageInfo(imgPath, index))
.then(({
imgPath,
imgInfo
}) => {
console.log();
// 根据画布的宽高计算出图片绘制的大小,这里会保证图片绘制不变形
let sx;
let sy;
const borderRadius = image.borderRadius || 0;
const setWidth = image.width;
const setHeight = image.height;
const width = this.toRpx(imgInfo.width);
const height = this.toRpx(imgInfo.height);
if (width / height <= setWidth / setHeight) {
sx = 0;
sy = (height - (width / setWidth) * setHeight) / 2;
} else {
sy = 0;
sx = (width - (height / setHeight) * setWidth) / 2;
}
if (!this.drawArr) this.drawArr = [];
this.drawArr.push({
type: "image",
borderRadius,
borderWidth: image.borderWidth,
borderColor: image.borderColor,
zIndex: typeof zIndex !== "undefined" ? zIndex : index,
imgPath,
sx,
sy,
sw: width - sx * 2,
sh: height - sy * 2,
x,
y,
w: setWidth,
h: setHeight,
});
resolve();
})
.catch((err) => reject(err));
});
},
/**
* 下载图片资源
* @param {*} imageUrl
*/
_downImage(imageUrl) {
return new Promise((resolve, reject) => {
if (imageUrl.includes('tmp') || imageUrl.includes('temp') || imageUrl.includes('wxfile')) {
// 支持本地地址
resolve(imageUrl); //2021/2/17 by cc
}
if (/^http/.test(imageUrl) && !new RegExp(wx.env.USER_DATA_PATH).test(imageUrl)) {
wx.downloadFile({
url: this._mapHttpToHttps(imageUrl),
success: (res) => {
if (res.statusCode === 200) {
resolve(res.tempFilePath);
} else {
reject(res.errMsg);
}
},
fail(err) {
reject(err);
},
});
} else {
// 支持本地地址
resolve(imageUrl);
}
});
},
/**
* 获取图片信息
* @param {*} imgPath
* @param {*} index
*/
_getImageInfo(imgPath, index) {
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: imgPath,
success(res) {
resolve({
imgPath,
imgInfo: res,
index
});
},
fail(err) {
reject(err);
},
});
});
},
toPx(rpx, int) {
if (int) {
return parseInt(rpx * this.factor * this.pixelRatio);
}
return rpx * this.factor * this.pixelRatio;
},
toRpx(px, int) {
if (int) {
return parseInt(px / (this.factor * this.pixelRatio));
}
return px / (this.factor * this.pixelRatio);
},
/**
* 将http转为https
* @param {String}} rawUrl 图片资源url
*/
_mapHttpToHttps(rawUrl) {
if (rawUrl.indexOf(":") < 0) {
return rawUrl;
}
const urlComponent = rawUrl.split(":");
if (urlComponent.length === 2) {
if (urlComponent[0] === "http") {
urlComponent[0] = "https";
return `${urlComponent[0]}:${urlComponent[1]}`;
}
}
return rawUrl;
},
};
Component({
properties: {},
created() {
const sysInfo = wx.getSystemInfoSync();
const {
pixelRatio,
screenWidth
} = sysInfo;
this.factor = screenWidth / 750;
this.pixelRatio = pixelRatio;
},
methods: Object.assign({
/**
* 计算画布的高度
* @param {*} config
*/
getHeight(config) {
const getTextHeight = (text) => {
let fontHeight = text.lineHeight || text.fontSize;
let height = 0;
if (text.baseLine === "top") {
height = fontHeight;
} else if (text.baseLine === "middle") {
height = fontHeight / 2;
} else {
height = 0;
}
return height;
};
const heightArr = [];
(config.blocks || []).forEach((item) => {
heightArr.push(item.y + item.height);
});
(config.texts || []).forEach((item) => {
let height;
if (Object.prototype.toString.call(item.text) === "[object Array]") {
item.text.forEach((i) => {
height = getTextHeight({
...i,
baseLine: item.baseLine,
});
heightArr.push(item.y + height);
});
} else {
height = getTextHeight(item);
heightArr.push(item.y + height);
}
});
(config.images || []).forEach((item) => {
heightArr.push(item.y + item.height);
});
(config.lines || []).forEach((item) => {
heightArr.push(item.startY);
heightArr.push(item.endY);
});
const sortRes = heightArr.sort((a, b) => b - a);
let canvasHeight = 0;
if (sortRes.length > 0) {
canvasHeight = sortRes[0];
}
if (config.height < canvasHeight || !config.height) {
return canvasHeight;
} else {
return config.height;
}
},
async create(config) {
await this.initCtx();
this.pixelRatio = config.pixelRatio || this.pixelRatio;
const height = this.getHeight(config);
this.initCanvas(config.width, height, config.debug)
.then(async () => {
this.node.width = this.data.pxWidth * this.pixelRatio;
this.node.height = this.data.pxHeight * this.pixelRatio;
this.ctx.scale(this.pixelRatio, this.pixelRatio);
// 设置画布底色
if (config.backgroundColor) {
this.ctx.save();
this.ctx.fillStyle = config.backgroundColor;
this.ctx.fillRect(0, 0, this.toPx(config.width), this.toPx(height));
this.ctx.restore();
}
const {
texts = [], blocks = [], lines = []
} = config;
if (!this.drawArr) this.drawArr = [];
const queue = this.drawArr
.concat(
texts.map((item) => {
item.type = "text";
item.zIndex = item.zIndex || 0;
return item;
})
)
.concat(
blocks.map((item) => {
item.type = "block";
item.zIndex = item.zIndex || 0;
return item;
})
)
.concat(
lines.map((item) => {
item.type = "line";
item.zIndex = item.zIndex || 0;
return item;
})
);
// 按照顺序排序
queue.sort((a, b) => a.zIndex - b.zIndex);
for (let i = 0, len = queue.length; i < len; i++) {
const item = queue[i];
if (item.type === "image") {
await this.drawImage(item);
} else if (item.type === "text") {
this.drawText(item);
} else if (item.type === "block") {
this.drawBlock(item);
} else if (item.type === "line") {
this.drawLine(item);
}
}
wx.canvasToTempFilePath({
canvas: this.node,
success: (res) => {
this.triggerEvent("success", res.tempFilePath);
},
fail: (err) => {
this.triggerEvent("fail", err);
},
},
this
);
})
.catch((err) => {
wx.showToast({
icon: "none",
title: err.errMsg || "生成失败",
});
console.error(err);
});
},
initCtx() {
return new Promise((resolve) => {
wx.createSelectorQuery()
.in(this)
.select("#canvasid")
.fields({
node: true,
})
.exec((res) => {
this.node = res[0].node;
this.ctx = this.node.getContext("2d");
resolve();
});
});
},
},
main,
handle,
helper
),
});
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/index/index.json
================================================
{
"component": true
}
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/index/index.wxml
================================================
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/index/index.wxss
================================================
.canvas {
width: 750rpx;
height: 750rpx;
}
.canvas.pro {
position: absolute;
bottom: 0;
left: 0;
transform: translate3d(-9999rpx, 0, 0);
}
.canvas.debug {
position: absolute;
bottom: 0;
left: 0;
border: 1rpx solid #ccc;
}
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/poster/index.js
================================================
Component({
properties: {
config: {
type: Object,
value: {},
},
preload: { // 是否预下载图片资源
type: Boolean,
value: false,
},
hideLoading: { // 是否隐藏loading
type: Boolean,
value: false,
}
},
ready() {
if (this.data.preload) {
const poster = this.selectComponent('#poster');
this.downloadStatus = 'doing';
poster.downloadResource(this.data.config).then(() => {
this.downloadStatus = 'success';
this.trigger('downloadSuccess');
}).catch((e) => {
this.downloadStatus = 'fail';
this.trigger('downloadFail', e);
});
}
},
methods: {
trigger(event, data) {
if (this.listener && typeof this.listener[event] === 'function') {
this.listener[event](data);
}
},
once(event, fun) {
if (typeof this.listener === 'undefined') {
this.listener = {};
}
this.listener[event] = fun;
},
downloadResource(reset) {
return new Promise((resolve, reject) => {
if (reset) {
this.downloadStatus = null;
}
const poster = this.selectComponent('#poster');
if (this.downloadStatus && this.downloadStatus !== 'fail') {
if (this.downloadStatus === 'success') {
resolve();
} else {
this.once('downloadSuccess', () => resolve());
this.once('downloadFail', (e) => reject(e));
}
} else {
poster.downloadResource(this.data.config)
.then(() => {
this.downloadStatus = 'success';
resolve();
})
.catch((e) => reject(e));
}
})
},
onCreate(reset = false) {
!this.data.hideLoading && wx.showLoading({
mask: true,
title: '生成中'
});
return this.downloadResource(typeof reset === 'boolean' && reset).then(() => {
!this.data.hideLoading && wx.hideLoading();
const poster = this.selectComponent('#poster');
poster.create(this.data.config);
})
.catch((err) => {
!this.data.hideLoading && wx.hideLoading();
wx.showToast({
icon: 'none',
title: err.errMsg || '生成失败'
});
console.error(err);
this.triggerEvent('fail', err);
})
},
onCreateSuccess(e) {
const {
detail
} = e;
this.triggerEvent('success', detail);
},
onCreateFail(err) {
console.error(err);
this.triggerEvent('fail', err);
}
}
})
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/poster/index.json
================================================
{
"component": true,
"usingComponents": {
"we-canvas": "../index/index"
}
}
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/poster/index.wxml
================================================
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/poster/index.wxss
================================================
================================================
FILE: miniprogram/cmpts/public/poster/wxa-plugin-canvas/poster/poster.js
================================================
const defaultOptions = {
selector: '#poster'
};
function Poster(options = {}, that) {
options = {
...defaultOptions,
...options,
};
const pages = getCurrentPages();
let ctx = pages[pages.length - 1];
if (that) ctx = that
const poster = ctx.selectComponent(options.selector);
delete options.selector;
return poster;
};
Poster.create = (reset = false, that) => {
const poster = Poster({}, that);
if (!poster) {
console.error('请设置组件的id="poster"!!!');
} else {
return Poster({}, that).onCreate(reset);
}
}
export default Poster;
================================================
FILE: miniprogram/cmpts/public/radio/radio_cmpt.js
================================================
Component({
externalClasses: ['outside-picker-multi-class'],
/**
* 组件的属性列表
*/
properties: {
sourceData: { //源数组
type: Array,
value: [],
},
sourceDataStr: { //源字符串 支持 x,y,z;;; 1=x,2=y,3=z
type: String,
value: '',
},
// 默认选中项
itemSelected: {
type: String,
value: '',
},
disabled: { // 是否禁用
type: Boolean,
value: false,
},
},
/**
* 生命周期方法
*/
lifetimes: {
attached: function () { },
ready: function () {
let sourceDataStr = this.data.sourceDataStr;
if (sourceDataStr) {
let options = [];
let arr = sourceDataStr.split(',');
for (let k = 0; k < arr.length; k++) {
let node = {};
if (arr[k].includes('=')) {
node.label = arr[k].split('=')[1];
node.value = arr[k].split('=')[0];
}
else {
node.label = arr[k];
node.value = arr[k];
}
options.push(node);
}
this.setData({
options
});
}
else {
let sourceData = this.data.sourceData;
let options = [];
for (let k = 0; k < sourceData.length; k++) {
let node = {
label: sourceData[k],
value: sourceData[k]
};
options.push(node);
}
this.setData({
options
});
}
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的初始数据
*/
data: {
options: []
},
/**
* 组件的方法列表
*/
methods: {
bindChange: function (e) {
this.triggerEvent('select', e.detail.value);
},
}
})
================================================
FILE: miniprogram/cmpts/public/radio/radio_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/radio/radio_cmpt.wxml
================================================
================================================
FILE: miniprogram/cmpts/public/radio/radio_cmpt.wxss
================================================
.radio-group {
width: 100%;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
padding: 0rpx 10rpx;
}
.radio-group .item {
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
line-height: 2.1;
min-height: 70rpx;
border-bottom: 1rpx solid #eee;
font-size: 28rpx;
}
.radio-group .item .item-label{
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.radio-group .item:last-child {
border-bottom: 0;
}
.radio-group .item:nth-child(odd) {
background-color: #fcfcfc;
}
.radio-group .item .item-radio {
margin-right: 20rpx;
padding-left: 10rpx;
}
================================================
FILE: miniprogram/cmpts/public/swiper/swiper_cmpt.js
================================================
const pageHelper = require('../../../helper/page_helper.js');
Component({
options: {
addGlobalClass: true,
},
/**
* 组件的属性列表
*/
properties: {
images: {
type: Array,
value: []
},
height: {
type: Number,
value: 350
},
mode: {
type: String,
value: 'aspectFill'
},
indicatorActiveColor: {
type: String,
value: '#000000'
},
interval: {
type: Number,
value: 3000
},
duration: {
type: Number,
value: 500
},
previousMargin: {
type: Number,
value: 0
},
nextMargin: {
type: Number,
value: 0
},
indicatorDots: {
type: Boolean,
value: true
},
autoplay: {
type: Boolean,
value: true
},
circular: {
type: Boolean,
value: true
},
vertical: {
type: Boolean,
value: false
},
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
url: function (e) {
pageHelper.url(e, this);
}
}
})
================================================
FILE: miniprogram/cmpts/public/swiper/swiper_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/swiper/swiper_cmpt.wxml
================================================
================================================
FILE: miniprogram/cmpts/public/swiper/swiper_cmpt.wxss
================================================
.swiper {
width: 100%;
}
.swiper-item-images {
width: 100%;
}
================================================
FILE: miniprogram/cmpts/public/table/table_cmpt.js
================================================
/**
*
*/
Component({
/**
* 外部样式类
*/
externalClasses: ['header-row-class-name', 'row-class-name', 'cell-class-name'],
/**
* 组件样式隔离
*/
options: {
styleIsolation: "isolated",
multipleSlots: true // 支持多个slot
},
/**
* 组件的属性列表
*/
properties: {
data: {
type: Array,
value: []
},
headers: {
type: Array,
value: []
},
// table的高度, 溢出可滚动
height: {
type: String,
value: 'auto'
},
width: {
type: Number || String,
value: '100%'
},
// 单元格的宽度
tdWidth: {
type: Number,
value: 35
},
// 固定表头 thead达到Header的位置时就应该被fixed了
offsetTop: {
type: Number,
value: 150
},
// 是否带有纵向边框
stripe: {
type: Boolean,
value: false
},
// 是否带有纵向边框
border: {
type: Boolean,
value: false
},
msg: {
type: String,
value: '暂无数据~'
}
},
/**
* 组件的初始数据
*/
data: {
scrolWidth: '20%'
},
lifetimes: {
attached: function () {
},
ready: function () {
},
detached: function () {
// 在组件实例被从页面节点树移除时执行
},
},
/**
* 组件的方法列表
*/
methods: {
}
})
================================================
FILE: miniprogram/cmpts/public/table/table_cmpt.json
================================================
{
"component": true,
"usingComponents": {}
}
================================================
FILE: miniprogram/cmpts/public/table/table_cmpt.wxml
================================================
{{ item.label }}
{{it[head["prop"]]}}
{{ msg }}
================================================
FILE: miniprogram/cmpts/public/table/table_cmpt.wxss
================================================
.reset {
background: white;
}
.other {
font-size: 20px;
}
.table {
position: relative;
font-size: 28rpx;
background: #fff;
border-right:none;
border-radius: 8rpx;
overflow: hidden;
}
.thead{
border-bottom: none;
display: flex;
justify-content: flex-start;
border-top-right-radius: 8rpx;
border-top-left-radius: 8rpx;
overflow: visible;
color: #909399;
border: 1px solid #ebeef5;
box-sizing: border-box;
}
.thead .td {
padding: 20rpx 10rpx;
font-weight: bold;
display: inline-block;
white-space:nowrap;
text-align: center;
border-right: 1rpx solid #fff;
}
.thead .td:last-child {
border-right: none;
}
.thead-border .td {
border-right: 1rpx solid #ebeef5;
}
.thead-border .td:last-child {
border-right: none;
}
/* .tr{
display: flex;
white-space:nowrap;
} */
.tbody {
box-sizing: border-box;
font-size: 28rpx;
color: #666;
border: 1px solid #ebeef5;
border-top: none;
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody-tr {
display: flex;
border-bottom: 1px solid #ebeef5;
}
.tbody-tr:last-child {
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody-tr-stripe {
background: #fff;
border-bottom: none;
}
.tbody-tr-stripe:nth-child(2n) {
background: #F6F6F6;
}
.tbody-tr .td {
white-space: wrap;
padding:20rpx 10rpx;
text-align: center;
}
.tbody-tr-border .td {
border-right: 1rpx solid #F6F6F6;
}
.tbody-tr-border .td:last-child {
border-right: none;
}
.no-data {
display: flex;
padding: 50rpx;
color: #666;
justify-content: center;
}
================================================
FILE: miniprogram/comm/behavior/about_bh.js
================================================
const pageHelper = require('../../helper/page_helper.js');
const cloudHelper = require('../../helper/cloud_helper.js');
module.exports = Behavior({
/**
* 页面的初始数据
*/
data: {
isLoad: false
},
methods: {
_loadDetail: async function (key,items) {
let title = '';
for (let k = 0; k < items.length; k++) {
if (items[k].key == key) {
title = items[k].title;
wx.setNavigationBarTitle({
title
});
if (key == 'SETUP_CONTENT_ABOUT') {
this.setData({
accountInfo: wx.getAccountInfoSync()
});
}
break;
}
}
let opts = {
title: 'bar'
}
let params = {
key
}
let about = await cloudHelper.callCloudData('home/setup_get', params, opts);
if (!about) {
about = [{ 'type': 'text', 'val': title }];
}
if (about) this.setData({
about,
isLoad: true
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
url: function (e) {
pageHelper.url(e, this);
}
}
})
================================================
FILE: miniprogram/comm/behavior/my_fav_bh.js
================================================
const pageHelper = require('../../helper/page_helper.js');
const cloudHelper = require('../../helper/cloud_helper.js');
module.exports = Behavior({
/**
* 页面的初始数据
*/
data: {
},
methods: {
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
},
myCommListListener: function (e) {
pageHelper.commListListener(this, e);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
url: function (e) {
pageHelper.url(e, this);
},
bindDelTap: async function (e) {
let oid = e.currentTarget.dataset.oid;
if (!oid) return;
let that = this;
let callback = async function () {
await cloudHelper.callCloudSumbit('fav/del', {
oid
}).then(res => {
pageHelper.delListNode(oid, that.data.dataList.list, 'FAV_OID');
that.data.dataList.total--;
that.setData({
dataList: that.data.dataList
});
pageHelper.showSuccToast('删除成功');
}).catch(err => {
console.log(err);
});
}
pageHelper.showConfirm('您确认删除?', callback);
}
}
})
================================================
FILE: miniprogram/comm/behavior/my_foot_bh.js
================================================
const FootBiz = require('../biz/foot_biz.js');
const pageHelper = require('../../helper/page_helper.js');
module.exports = Behavior({
/**
* 页面的初始数据
*/
data: {
},
methods: {
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
this._loadList();
},
_loadList: async function (e) {
let footList = FootBiz.getFootList();
this.setData({
footList
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadList();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
url: function (e) {
pageHelper.url(e, this);
}
}
})
================================================
FILE: miniprogram/comm/behavior/news_index_bh.js
================================================
const pageHelper = require('../../helper/page_helper.js');
module.exports = Behavior({
/**
* 页面的初始数据
*/
data: {
},
methods: {
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
_setCate(cateList, options, cateId = 0) {
if (!cateId) {
if (options && options.id) {
cateId = options.id;
}
}
this.setData({
_params: {
cateId,
}
});
for (let k = 0; k < cateList.length; k++) {
if (cateList[k].id == cateId) {
wx.setNavigationBarTitle({
title: cateList[k].title
});
if (cateList[k].style) { //样式
this.setData({
listMode: cateList[k].style
});
} else {
this.setData({
listMode: 'leftbig1'
});
}
}
}
}
}
})
================================================
FILE: miniprogram/comm/behavior/search_bh.js
================================================
const SearchBiz = require('../../comm/biz/search_biz.js');
const pageHelper = require('../../helper/page_helper.js');
module.exports = Behavior({
/**
* 页面的初始数据
*/
data: {
type: '', // 来自哪个业务标识
returnUrl: '', //搜索完返回哪个地址
cacheName: '', //本业务搜索历史缓存
search: '', //搜索关键字
hisKeys: []
},
methods: {
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
let type = options.type;
let returnUrl = options.returnUrl;
let cacheName = 'SERACH_HIS_' + type;
let hisKeys = SearchBiz.getHistory(cacheName);
if (hisKeys)
this.setData({
hisKeys
});
this.setData({
hisKeys,
type,
cacheName,
returnUrl
});
if (options && options.source)
this.setData({
source: options.source,
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: function (e) {
pageHelper.url(e, this);
},
/**
* 点击确认搜索
*/
bindSearchConfirm: function (e) {
if (!this.data.type) return;
let search = this.data.search.trim();
if (!search) return;
// 历史记录
let hisKeys = SearchBiz.addHistory(this.data.cacheName, search);
this.setData({
search,
hisKeys
});
let prevPage = pageHelper.getPrevPage();
// 直接调用上一个页面的setData()方法,把数据存到上一个页面中去
prevPage.setData({
search,
})
wx.navigateBack();
},
// 清空搜索记录
bindDelHisTap: function (e) {
SearchBiz.clearHistory(this.data.cacheName);
this.setData({
hisKeys: []
});
},
//清除关键字
bindClearKeyTap: function (e) {
this.setData({
search: ''
});
},
// 点击历史
bindKeyTap: function (e) {
let search = e.currentTarget.dataset.key.trim();
if (search) {
this.setData({
search
});
this.bindSearchConfirm(e);
}
}
}
})
================================================
FILE: miniprogram/comm/biz/admin_biz.js
================================================
/**
* Notes: 后台管理模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('./base_biz.js');
const cacheHelper = require('../../helper/cache_helper.js');
const cloudHelper = require('../../helper/cloud_helper.js');
const pageHelper = require('../../helper/page_helper.js');
const constants = require('../constants.js');
const setting = require('../../setting/setting.js');
class AdminBiz extends BaseBiz {
// 文章内容
static setContentDesc(that) {
let contentDesc = '未填写';
let content = that.data.formContent;
let imgCnt = 0;
let textCnt = 0;
for (let k = 0; k < content.length; k++) {
if (content[k].type == 'img') imgCnt++;
if (content[k].type == 'text') textCnt++;
}
if (imgCnt || textCnt) {
contentDesc = textCnt + '段文字,' + imgCnt + '张图片';
}
that.setData({
contentDesc
});
}
static async adminLogin(that, name, pwd) {
if (name.length < 5 || name.length > 30) {
wx.showToast({
title: '账号输入错误(5-30位)',
icon: 'none'
});
return;
}
if (pwd.length < 5 || pwd.length > 30) {
wx.showToast({
title: '密码输入错误(5-30位)',
icon: 'none'
});
return;
}
let params = {
name,
pwd
};
let opt = {
title: '登录中'
};
try {
await cloudHelper.callCloudSumbit('admin/login', params, opt).then(res => {
if (res && res.data && res.data.name)
cacheHelper.set(constants.CACHE_ADMIN, res.data, constants.ADMIN_TOKEN_EXPIRE);
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/admin/index/home/admin_home'),
});
});
} catch (e) {
console.log(e);
}
}
/**
* 清空管理员登录
*/
static clearAdminToken() {
cacheHelper.remove(constants.CACHE_ADMIN);
}
/**
* 获取管理员信息
*/
static getAdminToken() {
return cacheHelper.get(constants.CACHE_ADMIN);
}
/**
* 获取管理员电话
*/
static getAdminName() {
let admin = cacheHelper.get(constants.CACHE_ADMIN);
if (!admin) return '';
return admin.name;
}
/**
* 是否超级管理员
*/
static isSuperAdmin() {
let admin = cacheHelper.get(constants.CACHE_ADMIN);
if (!admin) return false;
return (admin.type == 1);
}
// 登录状态判定
static isAdmin(that, isSuper = false) {
wx.setNavigationBarColor({ //顶部
backgroundColor: '#2499f2',
frontColor: '#ffffff',
});
if (setting.IS_SUB) wx.hideHomeButton();
let admin = cacheHelper.get(constants.CACHE_ADMIN);
if (!admin) {
return wx.showModal({
title: '',
content: '登录已过期,请重新登录',
showCancel: false,
confirmText: '确定',
success: res => {
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/admin/index/login/admin_login'),
});
return false;
}
});
}
if (isSuper && admin.type != 1) {
return wx.showModal({
title: '',
content: '此功能需要超级管理员操作',
showCancel: false,
confirmText: '确定',
success: res => {
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/admin/index/home/admin_home'),
});
return false;
}
});
}
that.setData({
isAdmin: true,
isSuperAdmin: this.isSuperAdmin()
});
return true;
}
}
AdminBiz.CHECK_FORM_MGR_ADD = {
name: 'formName|must|string|min:5|max:30|name=账号',
desc: 'formDesc|must|string|max:30|name=姓名',
phone: 'formPhone|string|len:11|name=手机',
password: 'formPassword|must|string|min:6|max:30|name=密码',
};
AdminBiz.CHECK_FORM_MGR_EDIT = {
name: 'formName|must|string|min:5|max:30|name=账号',
desc: 'formDesc|must|string|max:30|name=姓名',
phone: 'formPhone|string|len:11|name=手机',
password: 'formPassword|string|min:6|max:30|name=新密码',
};
AdminBiz.CHECK_FORM_MGR_PWD = {
oldPassword: 'formOldPassword|must|string|min:6|max:30|name=旧密码',
password: 'formPassword|must|string|min:6|max:30|name=新密码',
password2: 'formPassword2|must|string|min:6|max:30|name=新密码再次填写',
};
module.exports = AdminBiz;
================================================
FILE: miniprogram/comm/biz/base_biz.js
================================================
/**
* Notes: 基础模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const pageHelper = require('../../helper/page_helper.js');
class BaseBiz {
static getCateName(cateId, cateList) {
for (let k = 0; k < cateList.length; k++) {
if (cateList[k].id == cateId) {
return cateList[k].title;
}
}
return '';
}
static getCateList(cateList) {
let arr = [];
for (let k = 0; k < cateList.length; k++) {
arr.push({
label: cateList[k].title,
type: 'cateId',
val: cateList[k].id, //for options form
value: cateList[k].id, //for list menu
})
}
return arr;
}
static setCateTitle(cateList) {
let curPage = pageHelper.getPrevPage(1);
if (!curPage) return;
let cateId = null;
if (curPage.options && curPage.options.id) {
cateId = curPage.options.id;
}
for (let k = 0; k < cateList.length; k++) {
if (cateList[k].id == cateId) {
wx.setNavigationBarTitle({
title: cateList[k].title
});
return;
}
}
}
}
module.exports = BaseBiz;
================================================
FILE: miniprogram/comm/biz/fav_biz.js
================================================
/**
* Notes: 预约模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-10 07:48:00
*/
const BaseBiz = require('./base_biz.js');
const cloudHelper = require('../../helper/cloud_helper.js');
const pageHelper = require('../../helper/page_helper.js');
class FavBiz extends BaseBiz {
static async isFav(that, oid) {
if (!oid) return;
that.setData({
isFav: -1
});
// 异步获取是否收藏
let params = {
oid
};
cloudHelper.callCloudSumbit('fav/is_fav', params, { title: 'bar' }).then(result => {
that.setData({
isFav: result.data.isFav
});
}).catch(error => { })
}
static async updateFav(that, oid, isFav, type, title) {
let path = pageHelper.getCurrentPageUrlWithArgs();
if (!oid || !path || !title || !type) return;
let params = {
oid,
title,
type,
path
}
let opts = {
title: (isFav == 0) ? '收藏中' : '取消中'
}
try {
let result = await cloudHelper.callCloudSumbit('fav/update', params, opts);
that.setData({
isFav: result.data.isFav,
});
} catch (e) {
console.log(e);
}
}
}
module.exports = FavBiz;
================================================
FILE: miniprogram/comm/biz/foot_biz.js
================================================
/**
* Notes: 足迹模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY www.code942.com
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('./base_biz.js');
const cacheHelper = require('../../helper/cache_helper.js');
const timeHelper = require('../../helper/time_helper.js');
const pageHelper = require('../../helper/page_helper.js');
const CACHE_FOOT = 'CACHE_FOOT';
class FootBiz extends BaseBiz {
static getFootList() {
let foot = cacheHelper.get(CACHE_FOOT);
if (foot) {
for (let i = 0; i < foot.length; i++) {
foot[i].time = timeHelper.timestamp2Time(foot[i].time);
}
}
return foot;
}
/**添加足迹缓存
*
* @param {*} key 键
* @param {*} val 值
* 格式 key:{
* type:类型
* title:标题
* time:加入时间
* }
* @param {*} size 最大个数
* @param {*} expire 过期时间
*/
static addFoot(type, title, size = 60, expire = 86400 * 365 * 3) {
let path = pageHelper.getCurrentPageUrlWithArgs();
if (!path || !title || !type) return [];
let foot = cacheHelper.get(CACHE_FOOT, []);
//查询是否存在 并删除
for (let k = 0; k < foot.length; k++) {
if (path == foot[k].path)
foot.splice(k, 1);
}
// 加到头部
let val = {
path,
type,
title,
time: timeHelper.time()
}
foot.unshift(val);
// 判断个数, 多的删除
if (foot.length > size)
foot.splice(foot.length - 1, 1);
// 存缓存
cacheHelper.set(CACHE_FOOT, foot, expire);
return foot;
}
}
module.exports = FootBiz;
================================================
FILE: miniprogram/comm/biz/passport_biz.js
================================================
/**
* Notes: 注册登录模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('./base_biz.js');
const cacheHelper = require('../../helper/cache_helper.js');
const cloudHelper = require('../../helper/cloud_helper.js');
const pageHelper = require('../../helper/page_helper.js');
const helper = require('../../helper/helper.js');
const constants = require('../constants.js');
class PassportBiz extends BaseBiz {
// 静默登录(有登录状态则不登录)
static async loginSilence(that) {
return await PassportBiz.loginCheck(false, 'slience', 'bar', that);
}
// 强制静默登录(有不论是否有登录状态)
static async loginSilenceMust(that) {
return await PassportBiz.loginCheck(false, 'must', 'bar', that);
}
// 必须登陆 可以取消(窗口形式)
static async loginMustCancelWin(that) {
return await PassportBiz.loginCheck(true, 'cancel', '', that);
}
// 必须登陆 只能强制注册或者回上页(窗口形式)
static async loginMustBackWin(that) {
return await PassportBiz.loginCheck(true, 'back', '', that);
}
// 获取token
static getToken() {
let token = cacheHelper.get(constants.CACHE_TOKEN);
return token || null;
}
// 设置token
static setToken(token) {
if (!token) return;
cacheHelper.set(constants.CACHE_TOKEN, token, constants.CACHE_TOKEN_EXPIRE);
}
// 获取user id
static getUserId() {
let token = cacheHelper.get(constants.CACHE_TOKEN);
if (!token) return '';
return token.id || '';
}
// 获取user name
static getUserName() {
let token = cacheHelper.get(constants.CACHE_TOKEN);
if (!token) return '';
return token.name || '';
}
static getStatus() {
let token = cacheHelper.get(constants.CACHE_TOKEN);
if (!token) return -1;
return token.status || -1;
}
// 是否登录
static isLogin() {
let id = PassportBiz.getUserId();
return (id.length > 0) ? true : false;
}
static loginStatusHandler(method, status) {
let content = '';
if (status == 0) content = '您的注册正在审核中,暂时无法使用此功能!';
else if (status == 8) content = '您的注册审核未通过,暂时无法使用此功能;请在个人中心修改资料,再次提交审核!';
else if (status == 9) content = '您的账号已经禁用, 无法使用此功能!';
if (method == 'cancel') {
wx.showModal({
title: '温馨提示',
content,
confirmText: '取消',
showCancel: false
});
}
else if (method == 'back') {
wx.showModal({
title: '温馨提示',
content,
confirmText: '返回',
showCancel: false,
success(result) {
wx.navigateBack();
}
});
}
return false;
}
// 登录判断及处理
static async loginCheck(mustLogin = false, method = 'back', title = '', that = null) {
let token = cacheHelper.get(constants.CACHE_TOKEN);
if (token && method != 'must') {
if (that)
that.setData({
isLogin: true
});
return true;
} else {
if (that) that.setData({
isLogin: false
});
}
let opt = {
title: title || '登录中',
};
let res = await cloudHelper.callCloudSumbit('passport/login', {}, opt).then(result => {
PassportBiz.clearToken();
if (result && helper.isDefined(result.data.token) && result.data.token && result.data.token.status == 1) {
PassportBiz.setToken(result.data.token);
if (that) that.setData({
isLogin: true
});
return true;
}
else if (mustLogin && result && helper.isDefined(result.data.token) && result.data.token && (result.data.token.status == 0 || result.data.token.status == 8 || result.data.token.status == 9)) {
let status = result.data.token.status;
return PassportBiz.loginStatusHandler(method, status);
}
else if (mustLogin && method == 'cancel') {
wx.showModal({
title: '温馨提示',
content: '此功能仅限注册用户',
confirmText: '马上注册',
cancelText: '取消',
success(result) {
if (result.confirm) {
let url = pageHelper.fmtURLByPID('/pages/my/reg/my_reg') + '?retUrl=back';
wx.navigateTo({ url });
} else if (result.cancel) {
}
}
});
return false;
}
else if (mustLogin && method == 'back') {
wx.showModal({
title: '温馨提示',
content: '此功能仅限注册用户',
confirmText: '马上注册',
cancelText: '返回',
success(result) {
if (result.confirm) {
let retUrl = encodeURIComponent(pageHelper.getCurrentPageUrlWithArgs());
let url = pageHelper.fmtURLByPID('/pages/my/reg/my_reg') + '?retUrl=' + retUrl;
wx.redirectTo({ url });
} else if (result.cancel) {
let len = getCurrentPages().length;
if (len == 1) {
let url = pageHelper.fmtURLByPID('/pages/default/index/default_index');
wx.reLaunch({ url });
}
else
wx.navigateBack();
}
}
});
return false;
}
else if (mustLogin && method == 'back') {
wx.showModal({
title: '温馨提示',
content: '此功能仅限注册用户',
confirmText: '马上注册',
cancelText: '返回',
success(result) {
if (result.confirm) {
let url = pageHelper.fmtURLByPID('/pages/my/reg/my_reg');
wx.reLaunch({ url });
} else if (result.cancel) {
wx.navigateBack();
}
}
});
return false;
}
}).catch(err => {
console.log(err);
PassportBiz.clearToken();
return false;
});
return res;
}
// 清除登录缓存
static clearToken() {
cacheHelper.remove(constants.CACHE_TOKEN);
}
// 手机号码
static async getPhone(e, that) {
if (e.detail.errMsg == "getPhoneNumber:ok") {
let cloudID = e.detail.cloudID;
let params = {
cloudID
};
let opt = {
title: '手机验证中'
};
await cloudHelper.callCloudSumbit('passport/phone', params, opt).then(res => {
let phone = res.data;
if (!phone || phone.length < 11)
wx.showToast({
title: '手机号码获取失败,请重新填写手机号码',
icon: 'none',
duration: 2000
});
else {
that.setData({
formMobile: phone
});
}
});
} else
wx.showToast({
title: '手机号码获取失败,请重新填写手机号码',
icon: 'none'
});
}
}
/** 表单校验 */
PassportBiz.CHECK_FORM = {
name: 'formName|must|string|min:1|max:30|name=昵称',
mobile: 'formMobile|must|len:11|name=手机',
forms: 'formForms|array'
};
module.exports = PassportBiz;
================================================
FILE: miniprogram/comm/biz/public_biz.js
================================================
/**
* Notes: 通用业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-05-22 07:48:00
*/
const BaseBiz = require('./base_biz.js');
const setting = require('../../setting/setting.js');
const cacheHelper = require('../../helper/cache_helper.js');
const dataHelper = require('../../helper/data_helper.js');
class PublicBiz extends BaseBiz {
/**
* 页面初始化
* @param {*} skin
* @param {*} that
*/
static initPageBase(that, { skin, isSetNavColor = true }) {
if (skin) {
skin.IS_SUB = setting.IS_SUB;
if ((setting.IS_SUB)) {
wx.hideHomeButton();
// 顶部
if (isSetNavColor)
wx.setNavigationBarColor({
backgroundColor: skin.NAV_BG,
frontColor: skin.NAV_COLOR,
});
}
that.setData({
skin
})
}
}
// 从富文本提取简介
static getRichEditorDesc(desc, content) {
if (desc) return dataHelper.fmtText(desc, 100);
if (!Array.isArray(content)) return desc;
for (let k = 0; k < content.length; k++) {
if (content[k].type == 'text') return dataHelper.fmtText(content[k].val, 100);
}
return desc;
}
static isCacheList(key) {
key = key.toUpperCase();
if (setting.CACHE_IS_LIST)
return cacheHelper.get(key + '_LIST');
else
return false;
}
static removeCacheList(key) {
key = key.toUpperCase();
if (setting.CACHE_IS_LIST)
cacheHelper.remove(key + '_LIST');
}
static setCacheList(key, time = setting.CACHE_LIST_TIME) {
key = key.toUpperCase();
if (setting.CACHE_IS_LIST)
cacheHelper.set(key + '_LIST', 'TRUE', time);
}
}
module.exports = PublicBiz;
================================================
FILE: miniprogram/comm/biz/search_biz.js
================================================
/**
* Notes: 搜索模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('./base_biz.js');
const cacheHelper = require('../../helper/cache_helper.js');
/**
*
*/
class SearchBiz extends BaseBiz {
static clearHistory(key){
cacheHelper.remove(key);
}
static getHistory(key)
{
return cacheHelper.get(key, []);
}
/**添加关键字缓存
*
* @param {*} key
* @param {*} val
* @param {*} size 个数
* @param {*} expire 过期时间
*/
static addHistory(key, val, size = 20, expire = 86400 * 30) {
if (!val || val.length == 0) return [];
let his = cacheHelper.get(key, []);
//查询是否存在 并删除
let pos = his.indexOf(val);
if (pos > -1) his.splice(pos, 1);
// 加到头部
his.unshift(val);
// 判断个数, 多的删除
if (his.length > size)
his.splice(his.length - 1, 1);
// 存缓存
cacheHelper.set(key, his, expire);
return his;
}
}
module.exports = SearchBiz;
================================================
FILE: miniprogram/comm/constants.js
================================================
/**
* Notes: 通用常量
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
module.exports = {
CACHE_TOKEN: 'CACHE_TOKEN', // 登录
CACHE_TOKEN_EXPIRE: 86400, //登录有效时间 秒
CACHE_ADMIN: 'ADMIN_TOKEN', // 管理员登录
ADMIN_TOKEN_EXPIRE: 3600 * 2, //管理员过期时间2小时有效 秒
}
================================================
FILE: miniprogram/helper/cache_helper.js
================================================
/**
* Notes: 微信缓存二次封装,有设置时效性的封装
* Ver : CCMiniCloud Framework 3.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const helper = require('./helper.js');
const TIME_SUFFIX = "_deadtime"
/**
* 设置
* k 键key
* v 值value
* t 秒
*/
function set(k, v, t = 86400 * 30) {
if (!k) return null;
wx.setStorageSync(k, v);
let seconds = parseInt(t);
if (seconds > 0) {
let newtime = Date.parse(new Date());
newtime = newtime / 1000 + seconds;
wx.setStorageSync(k + TIME_SUFFIX, newtime + "");
} else {
wx.removeStorageSync(k + TIME_SUFFIX);
}
}
/**
* 获取
* k 键key
* def 默认值
*/
function get(k, def = null) {
if (!k) return null;
let deadtime = wx.getStorageSync(k + TIME_SUFFIX);
if (!deadtime) return def;
deadtime = parseInt(deadtime);
if (!deadtime) return def;
if (deadtime) {
if (parseInt(deadtime) < Date.parse(new Date()) / 1000) {
wx.removeStorageSync(k);
wx.removeStorageSync(k + TIME_SUFFIX);
return def;
}
}
let res = wx.getStorageSync(k);
if (helper.isDefined(res)) {
return res;
} else {
return def;
}
}
/**
* 删除
*/
function remove(k) {
if (!k) return null;
wx.removeStorageSync(k);
wx.removeStorageSync(k + TIME_SUFFIX);
}
/**
* 清除所有key
*/
function clear() {
wx.clearStorageSync();
}
module.exports = {
set,
get,
remove,
clear
}
================================================
FILE: miniprogram/helper/cloud_helper.js
================================================
/**
* Notes: 云操作类库
* Ver : CCMiniCloud Framework 2.3.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2023-11-14 07:48:00
*/
const helper = require('./helper.js');
const dataHelper = require('./data_helper.js');
const cacheHelper = require('./cache_helper.js');
const constants = require('../comm/constants.js');
const contentCheckHelper = require('../helper/content_check_helper.js');
const pageHelper = require('../helper/page_helper.js');
const timeHelper = require('../helper/time_helper.js');
const setting = require('../setting/setting.js');
const CODE = {
SUCC: 200,
SVR: 500, //服务器错误
LOGIC: 1600, //逻辑错误
DATA: 1301, // 数据校验错误
HEADER: 1302, // header 校验错误
ADMIN_ERROR: 2401 //管理员错误
};
function callCloudSumbitAsync(route, params = {}, options) {
if (!helper.isDefined(options)) options = {
hint: false
}
if (!helper.isDefined(options.hint)) options.hint = false;
return callCloud(route, params, options)
}
async function callCloudSumbit(route, params = {}, options = { title: '提交中...' }) {
if (!helper.isDefined(options)) options = {
title: '提交中..'
}
if (!helper.isDefined(options.title)) options.title = '提交中..';
return await callCloud(route, params, options);
}
async function callCloudData(route, params = {}, options) {
if (!helper.isDefined(options)) options = {
title: '加载中..'
}
if (!helper.isDefined(options.title)) options.title = '加载中..';
let result = await callCloud(route, params, options).catch(err => {
return null;
});
if (result && helper.isDefined(result.data)) {
result = result.data;
if (Array.isArray(result)) {
// 数组处理
} else if (Object.keys(result).length == 0) {
result = null; //对象处理
}
}
return result;
}
function callCloud(route, params = {}, options) {
let title = '加载中';
let hint = true;
// 标题
if (helper.isDefined(options) && helper.isDefined(options.title))
title = options.title;
if (helper.isDefined(options) && helper.isDefined(options.hint))
hint = options.hint;
if (helper.isDefined(options) && helper.isDefined(options.doFail))
doFail = options.doFail;
if (hint) {
if (title == 'bar')
wx.showNavigationBarLoading();
else
wx.showLoading({
title: title,
mask: true
})
}
let token = '';
// 管理员token
if (route.indexOf('admin/') > -1) {
let admin = cacheHelper.get(constants.CACHE_ADMIN);
if (admin && admin.token) token = admin.token;
} else {
//正常用户
let user = cacheHelper.get(constants.CACHE_TOKEN);
if (user && user.id) token = user.id;
}
return new Promise(function (resolve, reject) {
let PID = pageHelper.getPID();
wx.cloud.callFunction({
name: 'mcloud',
data: {
route: route,
token,
PID,
params
},
success: function (res) {
if (res.result.code == CODE.LOGIC || res.result.code == CODE.DATA) {
console.log(res)
// 逻辑错误&数据校验错误
if (hint) {
wx.showModal({
title: '温馨提示',
content: res.result.msg,
showCancel: false
});
}
reject(res.result);
return;
} else if (res.result.code == CODE.ADMIN_ERROR) {
// 后台登录错误
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/admin/index/login/admin_login'),
});
//reject(res.result);
return;
} else if (res.result.code != CODE.SUCC) {
if (hint) {
wx.showModal({
title: '温馨提示',
content: '系统开小差了,请稍后重试',
showCancel: false
});
}
reject(res.result);
return;
}
resolve(res.result);
},
fail: function (err) {
if (hint) {
console.log(err)
if (err && err.errMsg && err.errMsg.includes('-501000') && err.errMsg.includes('Environment not found')) {
wx.showModal({
title: '',
content: '未找到云环境ID,请按手册检查前端配置文件setting.js的配置项【CLOUD_ID】或咨询作者微信cclinux0730',
showCancel: false
});
} else if (err && err.errMsg && err.errMsg.includes('-501000') && err.errMsg.includes('FunctionName')) {
wx.showModal({
title: '',
content: '云函数未创建或者未上传,请参考手册或咨询作者微信cclinux0730',
showCancel: false
});
} else if (err && err.errMsg && err.errMsg.includes('-501000') && err.errMsg.includes('performed in the current function state')) {
wx.showModal({
title: '',
content: '云函数正在上传中或者上传有误,请稍候',
showCancel: false
});
} else
wx.showModal({
title: '',
content: '网络故障,请稍后重试',
showCancel: false
});
}
reject(err.result);
return;
},
complete: function (res) {
if (hint) {
if (title == 'bar')
wx.hideNavigationBarLoading();
else
wx.hideLoading();
}
// complete
}
});
});
}
async function dataList(that, listName, route, params, options, isReverse = false) {
console.log('dataList begin');
if (!helper.isDefined(that.data[listName]) || !that.data[listName]) {
let data = {};
data[listName] = {
page: 1,
size: 20,
list: [],
count: 0,
total: 0,
oldTotal: 0
};
that.setData(data);
}
//改为后台默认控制
//if (!helper.isDefined(params.size))
// params.size = 20;
if (!helper.isDefined(params.isTotal))
params.isTotal = true;
let page = params.page;
let count = that.data[listName].count;
if (page > 1 && page > count) {
wx.showToast({
duration: 500,
icon: 'none',
title: '没有更多数据了',
});
return;
}
for (let key in params) {
if (!helper.isDefined(params[key]))
delete params[key];
}
let oldTotal = 0;
if (that.data[listName] && that.data[listName].total)
oldTotal = that.data[listName].total;
params.oldTotal = oldTotal;
// 云函数调用
await callCloud(route, params, options).then(function (res) {
console.log('cloud begin');
let dataList = res.data;
let tList = that.data[listName].list;
if (dataList.page == 1) {
tList = res.data.list;
} else if (dataList.page > that.data[listName].page) {
if (isReverse)
tList = res.data.list.concat(tList);
else
tList = tList.concat(res.data.list);
} else
return;
dataList.list = tList;
let listData = {};
listData[listName] = dataList;
that.setData(listData);
console.log('cloud END');
}).catch(err => {
console.log(err)
});
console.log('dataList END');
}
async function getTempFileURLOne(fileID) {
if (!fileID) return '';
let result = await wx.cloud.getTempFileURL({
fileList: [fileID],
})
if (result && result.fileList && result.fileList[0] && result.fileList[0].tempFileURL)
return result.fileList[0].tempFileURL;
return '';
}
async function transTempPics(imgList, dir, id, prefix = '') {
if (setting.IS_DEMO) return imgList;
if (prefix && !prefix.endsWith('_')) prefix += '_';
if (!id) id = timeHelper.time('YMD');
for (let i = 0; i < imgList.length; i++) {
let filePath = imgList[i];
let ext = filePath.match(/\.[^.]+?$/)[0];
// 是否为临时文件
if (filePath.includes('tmp') || filePath.includes('temp') || filePath.includes('wxfile')) {
let rd = prefix + dataHelper.genRandomNum(1000000, 9999999);
let cloudPath = id ? dir + id + '/' + rd + ext : dir + rd + ext;
cloudPath = pageHelper.getPID() + '/' + cloudPath;
await wx.cloud.uploadFile({
cloudPath,
filePath: filePath, // 文件路径
}).then(res => {
imgList[i] = res.fileID;
}).catch(error => {
// handle error TODO:剔除图片
console.error(error);
})
}
}
return imgList;
}
async function transRichEditorTempPics(content, dir, id, route) {
let imgList = [];
for (let k = 0; k < content.length; k++) {
if (content[k].type == 'img') {
imgList.push(content[k].val);
}
}
// 图片上传到云空间
imgList = await transTempPics(imgList, dir, id, 'rich');
// 更新图片地址
let imgIdx = 0;
for (let k = 0; k < content.length; k++) {
if (content[k].type == 'img') {
content[k].val = imgList[imgIdx];
imgIdx++;
}
}
// 更新本记录的图片信息
let params = {
id,
content
}
try {
await callCloudSumbit(route, params);
return content;
} catch (e) {
console.error(e);
}
return [];
}
async function transCoverTempPics(imgList, dir, id, route) {
// 图片上传到云空间
imgList = await transTempPics(imgList, dir, id, 'cover');
// 更新本记录的图片信息
let params = {
id,
imgList: imgList
}
try {
let res = await callCloudSumbit(route, params);
return res.data.urls;
} catch (err) {
console.error(err);
}
}
async function transFormsTempPics(forms, dir, id, route) {
wx.showLoading({
title: '提交中...',
mask: true
});
let hasImageForms = [];
for (let k = 0; k < forms.length; k++) {
if (forms[k].type == 'image') {
forms[k].val = await transTempPics(forms[k].val, dir, id, 'image');
hasImageForms.push(forms[k]);
}
else if (forms[k].type == 'content') {
let contentVal = forms[k].val;
for (let j in contentVal) {
if (contentVal[j].type == 'img') {
let ret = await transTempPics([contentVal[j].val], dir, id, 'content');
contentVal[j].val = ret[0];
}
}
hasImageForms.push(forms[k]);
}
}
if (hasImageForms.length == 0) return;
let params = {
id,
hasImageForms
}
try {
await callCloudSumbit(route, params);
} catch (err) {
console.error(err);
}
}
async function transTempPicOne(img, dir, id, isCheck = true) {
if (isCheck) {
wx.showLoading({
title: '图片校验中',
mask: true
});
let check = await contentCheckHelper.imgCheck(img);
if (!check) {
wx.hideLoading();
return pageHelper.showModal('不合适的图片, 请重新上传', '温馨提示');
}
wx.hideLoading();
}
let imgList = [img];
imgList = await transTempPics(imgList, dir, id);
if (imgList.length == 0)
return '';
else {
return imgList[0];
}
}
module.exports = {
CODE,
dataList,
callCloud,
callCloudSumbit,
callCloudData,
callCloudSumbitAsync,
transTempPics,
transRichEditorTempPics,
transCoverTempPics,
transFormsTempPics,
getTempFileURLOne,
transTempPicOne
}
================================================
FILE: miniprogram/helper/content_check_helper.js
================================================
/**
* Notes: UGC内容校验
* Ver : CCMiniCloud Framework 2.4.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2023-11-14 07:48:00
*/
const cloudHelper = require('../helper/cloud_helper.js');
const pageHelper = require('../helper/page_helper.js');
const setting = require('../setting/setting.js');
/**
* 图片类型校验
* @param {*} fileName
* @param {*} type
*/
function imgTypeCheck(path, type = ['jpg', 'jpeg', 'png','JPG','JPEG','PNG']) {
let fmt = path.split(".")[(path.split(".")).length - 1];
if (type.indexOf(fmt) > -1)
return true;
else
return false;
}
/**
* 图片大小校验
* @param {*} size
* @param {*} maxSize
*/
function imgSizeCheck(size, maxSize) {
return size < maxSize;
}
async function imgCheckCloud(path, opt) {
/*
let result = await cloudHelper.callCloudSumbit('check/img', params, opt).then(res => {
return true;
}).catch(err => {
return false;
});
*/
let result = await wx.cloud.callFunction({
name: 'cloud',
data: {
route: 'check/img',
token : '',
params:{img: wx.cloud.CDN( {
type: 'filePath',
filePath: path,
})
}
},
success: function (res) {
console.log(res)
console.log('succ')
return true;
},
fail: function (res) {
console.log(res)
return false;
},
complete: function (res) {
}
});
return result;
}
/**
* 图像校验
* @param {*} imgData
*/
async function imgCheck(imgData) {
let result = await wx.serviceMarket.invokeService({
service: 'wxee446d7507c68b11',
api: 'imgSecCheck',
data: {
"Action": "ImageModeration",
"Scenes": ["PORN", "POLITICS", "TERRORISM"],
"ImageUrl": new wx.serviceMarket.CDN({
type: 'filePath',
filePath: imgData,
}),
"ImageBase64": '',
"Config": "",
"Extra": ""
},
}).then(res => {
if (res && res.data && res.data.Response &&
res.data.Response.PornResult && res.data.Response.PornResult.Suggestion === 'PASS' &&
res.data.Response.PoliticsResult && res.data.Response.PoliticsResult.Suggestion === 'PASS' &&
res.data.Response.TerrorismResult && res.data.Response.TerrorismResult.Suggestion === 'PASS')
return true;
else
return false;
}).catch(err => {
console.log(err);
return false;
});
return result;
}
module.exports = {
imgCheck,
imgCheckCloud,
imgTypeCheck,
imgSizeCheck
}
================================================
FILE: miniprogram/helper/data_helper.js
================================================
/**
* Notes: 字符相关操作函数
* Ver : CCMiniCloud Framework 2.5.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
/**
* 生成一个特定范围内的随机数
*/
const genRandomNum = (min, max) => (Math.random() * (max - min + 1) | 0) + min;
/**
* 生成一个随机的数字字母字符串
*/
const genRandomString = len => {
const text = 'abcdefghijklmnopqrstuvwxyz0123456789';
const rdmIndex = text => Math.random() * text.length | 0;
let rdmString = '';
for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
return rdmString;
}
/**
* 生成一个随机的字母字符串
*/
const genRandomAlpha = len => {
const text = 'abcdefghijklmnopqrstuvwxyz';
const rdmIndex = text => Math.random() * text.length | 0;
let rdmString = '';
for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
return rdmString;
}
// 标签格式化
function fmtTag(tag) {
if (!tag || tag.constructor != String) return '';
tag = tag.trim();
tag = tag.replace(/,/g, ',');
let arr = tag.split(',');
let newArr = [];
for (let k = 0; k < arr.length; k++) {
arr[k] = arr[k].trim();
if (arr[k].length > 0) newArr.push(arr[k]);
}
return newArr.join(',');
}
// 拆分一维数组为二维数组
function spArr(arr, size) {
if (!arr || !Array.isArray(arr) || arr.length == 0) return arr;
let newArray = [];
let index = 0;
while (index < arr.length) {
newArray.push(arr.slice(index, index += size));
}
return newArray;
}
/**
* 把字符串格式化为数组
* @param {*} str
* @param {*} sp
*/
function str2Arr(str, sp = ',') {
if (str && Array.isArray(str)) return str;
str = str.replace(/,/g, sp);
let arr = str.split(sp);
for (let i = 0; i < arr.length; i++) {
arr[i] = arr[i].trim();
if (isNumber(arr[i])) {
arr[i] = Number(arr[i]);
}
}
return arr;
}
/**
* 校验只要是数字(包含正负整数,0以及正负浮点数)就返回true
* @param {*} val
* @returns bool
*/
function isNumber(val) {
var reg = /^[0-9]+.?[0-9]*$/;
if (reg.test(val)) {
return true;
} else {
return false;
}
}
/**
* 提取对象数组的某个属性数组,如[{'x':1},{'x':2}] 提取 x得到[1,2]
* @param {*} arr
* @param {*} key
* @returns []
*/
function getArrByKey(arr, key) {
if (!Array.isArray(arr)) return;
return arr.map((item) => {
return item[key]
});
}
/**
* 提取对象数组的多个属性数组,
* 如 [{'x':1,'y':11,'z':111},{'x':2,'y':22,'z':222}]
* 提取 ['x','y'] 得到[{'x':1,'y':11},{'x':2,'y':22}]
* @param {*} arr
* @param {*} keys
* @returns []
*/
function getArrByKeyMulti(arr, keys = []) {
if (!Array.isArray(arr)) return;
if (!Array.isArray(keys)) return;
let ret = [];
for (let k = 0; k < arr.length; k++) {
let node = {};
for (let j in keys) {
node[keys[j]] = arr[k][keys[j]];
}
ret.push(node);
}
return ret;
}
/**
* 提取对象数组某个键值等于某值的对象数据
* @param {*} arr
* @param {*} key
* @param {*} val
* @returns object {}
*/
function getDataByKey(arr, key, val) {
if (!Array.isArray(arr)) return null;
for (let k = 0; k < arr.length; k++) {
if (arr[k][key] == val)
return arr[k];
}
return null;
}
/**
* 文本内容格式化处理
* @param {*} content
* @param {*} len 截取长度 -1不截取
*/
function fmtText(content, len = -1) {
if (!content) return content;
let str = content.replace(/[\r\n]/g, ""); //去掉回车换行
if (len > 0) {
str = str.substr(0, len);
}
return str.trim();
}
// 下划线转换驼峰
function toHump(name) {
name = name.replace(/\_(\w)/g, function (all, letter) {
return letter.toUpperCase();
});
// 首字母大写
let firstChar = name.charAt(0).toUpperCase();
return firstChar + name.slice(1);
}
// 驼峰转换下划线
function toLine(name) {
name = name.replace(/([A-Z])/g, "_$1").toLowerCase();
//如果首字符为下划线,干掉
if (name.charAt(0) === '_')
return name.slice(1);
else
return name;
}
// 金额格式化 dot为金额每隔三位用","或" "间隔
function fmtMoney(s, dot = ',', prefix = '¥') {
if (s === '' || s === null || s === undefined) s = 0;
s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(2) + "";
var l = s.split(".")[0].split("").reverse(),
r = s.split(".")[1];
t = "";
for (i = 0; i < l.length; i++) {
t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? dot : "");
}
return prefix + t.split("").reverse().join("") + "." + r;
}
/**
*简单数组转对象数组
* @param {*} arr [1,2,3]
* @param {*} key [x1,x2,x3]
* @returns [{x1:1,x2:1,x3:1},{x1:2,x2:2,x3:2},{x1:3,x2:3,x3:3}]
*/
function arr2ObjectArr(arr, key1, key2, key3) {
let ret = [];
for (let k = 0; k < arr.length; k++) {
let obj = {};
if (key1) obj[key1] = arr[k];
if (key2) obj[key2] = arr[k];
if (key3) obj[key3] = arr[k];
ret.push(obj);
}
return ret;
}
/**
* property
* @param {*} property 排序属性
* @returns 排序好的数组
* 用法 arr.sort(compare('age'))
*/
function objArrSortAsc(property) {
return function (a, b) {
var value1 = a[property];
var value2 = b[property];
if (value1 < value2)
return -1;
else if (value1 > value2)
return 1;
else return 0;
}
}
/**
* property
* @param {*} property 排序属性
* @returns 排序好的数组
* 用法 arr.sort(compare('age'))
*/
function objArrSortDesc(property) {
return function (a, b) {
var value1 = a[property];
var value2 = b[property];
if (value1 < value2)
return 1;
else if (value1 > value2)
return -1;
else return 0;
}
}
/**
* 数组有则减少,无则增加
* @param {*} arr
* @param {*} data
* @param {*} sort 排序方式 asc/desc
*/
function arrAddDel(arr, data, sort = 'asc') {
if (!arr) return arr;
if (!Array.isArray(arr)) return arr;
let idx = arr.indexOf(data);
if (idx > -1)
arr.splice(idx, 1);
else
arr.push(data)
if (sort == 'asc')
return arr.sort();
else
return arr.reverse();
}
//数据深度拷贝
function deepClone(data) {
if (data === null || typeof data === 'string' || typeof data === 'number' || typeof data === 'boolean' || typeof data === 'undefined') {
return data;
}
return JSON.parse(JSON.stringify(data));
}
function padLeft(str, len, charStr) {
if (!str)
str = '';
else
str = str + '';
return new Array(len - str.length + 1).join(charStr || '') + str;
}
function padRight(str, len, charStr) {
if (!str)
str = '';
else
str = str + '';
return str + new Array(len - str.length + 1).join(charStr || '');
}
// 选项表单处理
function getSelectOptions(str) {
if (!str)
return [];
else if (str.includes('=')) {
let arr = str.split(',');
for (let k = 0; k < arr.length; k++) {
let node = arr[k].split('=');
arr[k] = {};
let labelArr = node[1].split('|');
arr[k].label = labelArr[0];
if (labelArr.length > 1) { //扩展属性
arr[k].ext = labelArr[1];
}
arr[k].val = node[0];
}
return arr;
} else {
return str.split(',');
}
}
// 数组元素交换位置 index1和index2分别是两个数组的索引值
function arraySwap(arr, index1, index2) {
arr[index1] = arr.splice(index2, 1, arr[index1])[0];
return arr;
}
// 数组置顶
function arrayTop(arr, idx) {
let node = arr.splice(idx, 1)[0];
arr.unshift(node);
return arr;
}
// 数组置底
function arrayBottom(arr, idx) {
let node = arr.splice(idx, 1)[0];
arr.push(node);
return arr;
}
/**
* 把某个值/对象按key插到某个对象数组
* @param {*} arr 目标数组
* @param {*} key 键
* @param {*} val 判断值
* @param {*} obj 插入对象{}
*/
function insertObjArrByKey(arr, key, val, obj) {
if (!arr) return arr;
for (let k = 0; k < arr.length; k++) {
if (arr[k][key] == val) {
// 发现存在
arr[k].list.push(obj);
return arr;
}
}
// 不存在
let newObj = {
[key]: val,
list: [obj]
}
arr.push(newObj);
return arr;
}
/**
* 从对象数组中, 根据某个键值 获取满足的对象
* @param {*} arr
* @param {*} key
* @param {*} val
*/
function getValFromArr(arr, key = 'val', val = '') {
if (!Array.isArray(arr)) return null;
for (let k = 0; k < arr.length; k++) {
if (arr[k][key] == val)
return arr[k];
}
return null;
}
module.exports = {
arrayTop,
arraySwap,
arrayBottom,
fmtTag,
getValFromArr,
getArrByKey,
getArrByKeyMulti, //提取对象数组的多个属性数组
spArr, //拆分一维数组为二维
getDataByKey,
str2Arr,
arr2ObjectArr,
insertObjArrByKey,
arrAddDel,
objArrSortAsc,
objArrSortDesc,
arrAddDel,
isNumber,
padLeft,
padRight,
genRandomString, // 随机字符串
genRandomAlpha,
genRandomNum, // 随机数字
fmtText, // 文本内容格式化处理
fmtMoney, //金额格式化
toHump,
toLine,
getSelectOptions, //选项表单处理
deepClone
}
================================================
FILE: miniprogram/helper/file_helper.js
================================================
/**
* Notes: 文件处理相关函数
* Ver : CCMiniCloud Framework 2.7.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-02-05 04:00:00
*/
const pageHelper = require('./page_helper.js');
const timeHelper = require('./time_helper.js');
function openDoc(name, url, ext = '.xlsx') {
wx.showLoading({
title: '文件下载中',
});
wx.downloadFile({
url,
//fileID:' ',
filePath: wx.env.USER_DATA_PATH + '/' + name + timeHelper.time('YMDhms') + ext,
success: function (res) {
wx.hideLoading();
if (res.statusCode != 200)
return pageHelper.showModal('打开文件失败,请重试或者采取别的下载方式');
const filePath = res.filePath;
wx.openDocument({
showMenu: true,
filePath: filePath,
success: function (res) {
console.log('打开文档成功');
}
})
},
fail: function (err) {
wx.hideLoading();
console.log(err);
pageHelper.showModal('打开文件失败,请重试或者采取别的下载方式');
}
})
}
module.exports = {
openDoc
}
================================================
FILE: miniprogram/helper/form_helper.js
================================================
/**
* Notes: 表单通用类库
* Ver : CCMiniCloud Framework 2.8.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-02-28 07:48:00
*/
/**
* model变表单
* @param {*} model
*/
function model2Form(model) {
let newModel = {};
for (let key in model) {
let arr = key.split('_');
let result = '';
for (let i = 1; i < arr.length; i++) {
let name = arr[i].toLowerCase();
name = name.charAt(0).toUpperCase() + name.slice(1);
result = result + name;
}
newModel['form' + result] = model[key];
}
return newModel;
}
// picker表单赋值到页面data
function setOptions(that, options, name, val) {
let idx = options.indexOf(val);
idx = (idx < 0) ? 0 : idx;
that.setData({
[name]: idx
})
}
module.exports = {
model2Form,
setOptions
}
================================================
FILE: miniprogram/helper/helper.js
================================================
/**
* Notes: 通用类库
* Ver : CCMiniCloud Framework 2.9.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
/**
* 判断变量,参数,对象属性是否定义
* @param {*} val
*/
function isDefined(val) {
// == 不能判断是否为null
if (val === undefined)
return false;
else
return true;
}
/**
* 判断对象是否为空
* @param {*} obj
*/
function isObjectNull(obj) {
return (Object.keys(obj).length == 0);
}
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
};
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
/**
* 从picker options中 获取索引值
* @param {*} options
* [{
value: 0,
label: '猎头'
}]
* @param {*} val
*/
function getOptionsIdx(options, val) {
for (let i = 0; i < options.length; i++) {
if (options[i].value === val)
return i;
}
return 0;
}
module.exports = {
isDefined,
isObjectNull,
sleep,
getOptionsIdx,
}
================================================
FILE: miniprogram/helper/mini_helper.js
================================================
/**
* Notes: 软硬件系统相关函数
* Ver : CCMiniCloud Framework 2.11.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-01-25 29:00:00
*/
function getAuth(auth, authName, callback) {
auth = 'scope.' + auth;
wx.getSetting({
success: res => {
console.log(res)
if (res.authSetting[auth]) {
console.log('true');
callback && callback();
} else if (res.authSetting[auth] === undefined) {
// 未做任何授权
wx.showModal({
title: '提示',
mask: true,
content: '您尚未开启' + authName + '的权限,请点击确定去开启权限!',
success: (res) => {
if (res.confirm) {
wx.authorize({
scope: auth,
success: (res) => {
console.log('授权成功', res);
callback && callback();
},
fail: (res) => {
console.log('您没有授权 fail=', res);
wx.showToast({
mask: true,
title: '您没有授权,无法' + authName,
icon: 'none'
});
}
});
} else {
wx.showToast({
mask: true,
title: '您没有授权,无法' + authName,
icon: 'none'
});
}
}
});
} else {
// 已经禁止
wx.showModal({
title: '提示',
content: '您未开启' + authName + '的权限,请点击确定去开启权限!',
success: (res) => {
if (res.confirm) {
wx.openSetting({
success: (res) => {
wx.showToast({
mask: true,
icon: 'none',
title: '正在' + authName,
});
if (res.authSetting[auth]) {
console.log('false success res=', res);
callback && callback();
} else {
wx.showToast({
mask: true,
title: '您没有授权,无法' + authName + '!',
icon: 'none'
});
}
},
fail: (res) => {
console.log('false file res=', res);
}
});
} else {
wx.showToast({
mask: true,
title: '您没有授权,无法' + authName,
icon: 'none'
});
}
}
});
}
}
});
}
module.exports = {
getAuth
}
================================================
FILE: miniprogram/helper/page_helper.js
================================================
/**
* Notes: 通用页面操作类库
* Ver : CCMiniCloud Framework 2.0.11 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const helper = require('./helper.js');
const setting = require('../setting/setting.js');
const cacheHelper = require('./cache_helper.js');
const picHelper = require('./pic_helper.js');
// 加入手机日程 单位秒
function addPhoneCalendar(title, startTime, endTime, alarmOffset = 3600) {
wx.addPhoneCalendar({
title,
startTime,
endTime,
// description: "这是日程内容",
alarm: 'true',
alarmOffset, //提前时间,秒
success: () => {
pageHelper.showSuccToast('添加成功');
},
fail: (res) => {
if (res && res.errMsg && res.errMsg.includes('refuesed')) {
pageHelper.showModal('请在手机的"设置›微信" 选项中,允许微信访问你的日历', '日历权限未开启')
}
},
complete: (res) => {
console.log(res)
}
});
}
// 自定义导航高度
function getCustomNavHeight() {
let sysInfo = wx.getSystemInfoSync();
let menuInfo = wx.getMenuButtonBoundingClientRect();
let navigationBarHeight = menuInfo.top + menuInfo.bottom - sysInfo.statusBarHeight;
return navigationBarHeight;
}
function getCurrentPageURL() {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
const url = `/${currentPage.route}`;
return url;
}
function getCurrentPageUrlWithArgs() {
let pages = getCurrentPages();
let currentPage = pages[pages.length - 1];
let url = currentPage.route;
let options = currentPage.options;
let urlWithArgs = url + '?';
for (let key in options) {
let value = options[key];
urlWithArgs += key + '=' + value + '&';
}
urlWithArgs = urlWithArgs.substring(0, urlWithArgs.length - 1);
return '/' + urlWithArgs;
}
function getPID() {
let route = getCurrentPageURL();
let PID = route.replace('/projects/', '');
PID = PID.split('/')[0];
return PID;
}
function fmtURLByPID(url, PID = '') {
if (!PID) PID = getPID();
if (url.startsWith('/pages/')) {
url = url.replace('/pages/', '/projects/' + PID + '/pages/');
} else {
url = '/projects/' + PID + '/' + url;
}
return url;
}
/** 定时器销毁 */
function clearTimer(that, timerName = 'timer') {
if (helper.isDefined(that.data[timerName])) {
clearInterval(that.data[timerName]);
}
}
/**
* 获取父页面
* @param {*} deep 1=当前 2=父页 3=父父页
*/
function getPrevPage(deep = 2) {
let pages = getCurrentPages();
let prevPage = pages[pages.length - deep]; //上一个页面
return prevPage;
}
/**
* 修改当前/父页面的某个列表节点
* @param {*} id 主键
* @param {*} valName 被修改的字段名
* @param {*} val 被修改的值
* @param {*} list 数据集
* @param {*} idName 主键名
*/
function modifyListNode(id, list, valName, val, idName = '_id') {
if (!list || !Array.isArray(list)) return false;
let pos = list.findIndex(item => item[idName] === id);
if (pos > -1) {
list[pos][valName] = val;
return true;
}
return false;
}
/**
* 修改当前/父页面的某个列表节点(单个值)
* @param {*} id 主键
* @param {*} valName 被修改的字段名
* @param {*} val 被修改的值
* @param {*} deep 1=当前 2=父页 3=父父页
* @param {*} listName 数据集名
* @param {*} idName 主键名
*/
function modifyPrevPageListNode(id, valName, val, deep = 2, listName = 'dataList', idName = '_id') {
let prevPage = getPrevPage(deep);
if (!prevPage) return;
let dataList = prevPage.data[listName];
if (!dataList) return;
let list = dataList['list'];
if (modifyListNode(id, list, valName, val, idName)) {
prevPage.setData({
[listName + '.list']: list
});
}
}
/**
* 修改当前/父页面的某个列表节点(一组值)
* @param {*} id 主键
* @param {*} valName 被修改的字段名
* @param {*} val 被修改的值
* @param {*} deep 1=当前 2=父页 3=父父页
* @param {*} listName 数据集名
* @param {*} idName 主键名
*/
function modifyPrevPageListNodeObject(id, vals, deep = 2, listName = 'dataList', idName = '_id') {
let prevPage = getPrevPage(deep);
if (!prevPage) return;
let dataList = prevPage.data[listName];
if (!dataList) return;
let list = dataList['list'];
for (let key in vals) {
modifyListNode(id, list, key, vals[key], idName)
}
prevPage.setData({
[listName + '.list']: list
});
}
/**
* 从记录数组里删除某个节点
* @param {*} id
* @param {*} list
* @param {*} idName
*/
function delListNode(id, list, idName = '_id') {
if (!list || !Array.isArray(list)) return false;
let pos = list.findIndex(item => item[idName] === id);
if (pos > -1) {
list.splice(pos, 1);
return true;
}
return false;
}
/**
* 删除当前/父页面的某个列表节点
* @param {*} id 主键
* @param {*} deep 1=当前 2=父页 3=父父页
* @param {*} listName 数据集名
* @param {*} idName 主键名
*/
function delPrevPageListNode(id, deep = 2, listName = 'dataList', idName = '_id') {
let prevPage = getPrevPage(deep);
let dataList = prevPage.data[listName];
if (!dataList) return;
let list = dataList['list'];
let total = dataList['total'] - 1;
if (delListNode(id, list, idName)) {
prevPage.setData({
[listName + '.list']: list,
[listName + '.total']: total
});
}
}
/**
* 刷新当前/父页面的某个列表节点
* @param {*} deep 1=当前 2=父页 3=父父页
* @param {*} listName 数据集名
* @param {*} listFunc 翻页函数名
*/
async function refreshPrevListNode(deep = 2, listName = 'dataList', listFunc = '_getList') {
let prevPage = getPrevPage(deep);
let dataList = prevPage.data[listName];
if (!dataList) return;
await prevPage[listFunc]();
}
/**
* 回到顶部测算
*/
function scrollTop(e, that) {
if (e.scrollTop > 100) {
that.setData({
topShow: true
});
} else {
that.setData({
topShow: false
});
}
}
/**
* 删除图片
* @param {*} that
* @param {*} idx 被删除图片索引
* @param {*} imgListName 图片数组名
*/
function delImage(that, idx, imgListName = 'imgList') {
let callback = function () {
that.data[imgListName].splice(idx, 1);
that.setData({
[imgListName]: that.data[imgListName]
})
}
showConfirm('确定要删除该图片吗?', callback);
}
/**
* 图片预览
* @param {*} that
* @param {*} url
* @param {*} imgListName 图片数组名
*/
function previewImage(that, url, imgListName = 'imgList') {
// 图片预览
wx.previewImage({
urls: that.data[imgListName],
current: url
});
}
/**
* 取得data-数据 去掉驼峰式命名,改成纯小写式命名
* @param {*} e
* @param {*} name
* @param {*} child 是否获取穿透子元素的data-
*/
function dataset(e, name, child = false) {
if (!child)
return e.currentTarget.dataset[name];
else
return e.target.dataset[name];
}
// 表单的双向数据绑定
function model(that, e) {
let item = e.currentTarget.dataset.item;
that.setData({
[item]: e.detail.value
})
}
// 表单的开关按钮数据绑定 mode=int/bool
function switchModel(that, e, mode = 'int') {
let item = e.currentTarget.dataset.item;
let sel = (e.detail.value) ? 1 : 0;
if (mode == 'bool') {
sel = (e.detail.value) ? true : false;
}
that.setData({
[item]: sel
})
}
// 无提示成功,同时做后续处理, 最多可显示两行
function showNoneToast(title = '操作完成', duration = 1500, callback) {
return wx.showToast({
title: title,
icon: 'none',
duration: duration,
mask: true,
success: function () {
callback && (setTimeout(() => {
callback();
}, duration));
}
});
}
// 无提示成功,返回
function showNoneToastReturn(title = '操作完成', duration = 2000) {
let callback = function () {
wx.navigateBack();
}
return showNoneToast(title, duration, callback);
}
// 错误提示成功,同时做后续处理, 最多显示7个汉字长度
function showErrToast(title = '操作失败', duration = 1500, callback) {
return wx.showToast({
title: title,
icon: 'error',
duration: duration,
mask: true,
success: function () {
callback && (setTimeout(() => {
callback();
}, duration));
}
});
}
// 加载中,同时做后续处理, 最多显示7个汉字长度
function showLoadingToast(title = '加载中', duration = 1500, callback) {
return wx.showToast({
title: title,
icon: 'loading',
duration: duration,
mask: true,
success: function () {
callback && (setTimeout(() => {
callback();
}, duration));
}
});
}
// 提示成功,同时做后续处理, 最多显示7个汉字长度
function showSuccToast(title = '操作成功', duration = 1500, callback) {
return wx.showToast({
title: title,
icon: 'success',
duration: duration,
mask: true,
success: function () {
callback && (setTimeout(() => {
callback();
}, duration));
}
});
}
// 提示成功,同时返回
function showSuccToastReturn(title = '操作成功', duration = 1500) {
let callback = function () {
wx.navigateBack();
}
return showSuccToast(title, duration, callback);
}
// 清理提示焦点
function formClearFocus(that) {
let data = that.data;
let focus = {};
for (let key in data) {
if (key.startsWith('form') && !key.endsWith('Focus'))
focus[key + 'Focus'] = null;
}
that.setData({
...focus
});
}
// 焦点提示
function formHint(that, formName, hint) {
that.setData({
[formName + 'Focus']: hint
});
return showModal(hint);
}
// 二次确认操作
function showConfirm(title = '确定要删除吗?', yes, no) {
return wx.showModal({
title: '',
content: title,
cancelText: '取消',
confirmText: '确定',
success: res => {
if (res.confirm) {
yes && yes();
} else if (res.cancel) {
no && no();
}
}
})
}
function showModal(content, title = '温馨提示', callback = null, confirmText = null) {
return wx.showModal({
title,
content: content,
confirmText: confirmText || '确定',
showCancel: false,
success(res) {
callback && callback();
}
});
}
/**
* 页面赋值
* @param {*} that
* @param {*} data
*/
function setPageData(that, data) {
// 删除页面保留数据
if (helper.isDefined(data['__webviewId__']))
delete data['__webviewId__'];
that.setData(data);
}
/**
* 配合搜索列表响应监听
* @param {*} that
*/
function commListListener(that, e) {
if (helper.isDefined(e.detail.search))
that.setData({
search: '',
sortType: '',
});
else {
that.setData({
dataList: e.detail.dataList,
});
if (e.detail.sortType)
that.setData({
sortType: e.detail.sortType,
});
}
}
function bindShowModalTap(e) {
this.setData({
modalName: e.currentTarget.dataset.modal
})
}
function bindHideModalTap(e) {
this.setData({
modalName: null
})
}
/**
* 控制回页首按钮
* @param {*} e
*/
function showTopBtn(e, that) {
if (e.scrollTop > 100) {
that.setData({
topBtnShow: true
});
} else {
that.setData({
topBtnShow: false
});
}
}
/**
* 回到顶部
*/
function top() {
wx.pageScrollTo({
scrollTop: 0
})
}
// 跳到锚点
function anchor(id, that) {
try {
let query = wx.createSelectorQuery().in(that);
query.selectViewport().scrollOffset()
//#comm 跳转到指定id位置
query.select('#' + id).boundingClientRect();
query.exec(function (res) {
if (!res || res.length != 2 || !res[0] || !res[1]) return;
//第一个为视图,第二个为当前id
let miss = res[0].scrollTop + res[1].top - 10;
wx.pageScrollTo({
scrollTop: miss,
duration: 300
});
});
}
catch (err) {
console.error(err);
}
}
// 页面跳转/图片预览
function url(e, that) {
let url = e.currentTarget.dataset.url;
let type = e.currentTarget.dataset.type;
if (!type) type = 'url';
switch (type) {
case 'picker': {
//picker 选择trigger
let item = e.currentTarget.dataset.item;
that.setData({
[item]: e.detail
})
break;
}
case 'top': {
top();
break;
}
case 'mini': {
wx.navigateToMiniProgram({
appId: e.currentTarget.dataset.app,
path: url,
envVersion: 'release'
});
break;
}
case 'redirect': {
if (!url) return;
wx.redirectTo({
url
});
break;
}
case 'reLaunch':
case 'relaunch': {
if (!url) return;
wx.reLaunch({
url
})
break;
}
case 'copy': {
wx.setClipboardData({
data: url,
success(res) {
wx.getClipboardData({
success(res) {
showNoneToast('已复制到剪贴板');
}
})
}
});
break;
}
case 'hint': {
if (!url) return;
showModal(url);
break;
}
case 'switch': {
if (!url) return;
wx.switchTab({
url
});
break;
}
case 'back': {
wx.navigateBack();
break;
}
case 'toURL': {
toURL(url);
break;
}
case 'phone': {
wx.makePhoneCall({
phoneNumber: url
});
break;
}
case 'anchor': {
anchor(url, that);
break;
}
case 'saveimg':
case 'saveimage': {
let callback = function () {
wx.saveImageToPhotosAlbum({ //成功之后保存到本地
filePath: url, //生成的图片的本地路径
success: function (res) {
wx.showToast({
title: e.currentTarget.dataset.hint || '保存成功',
icon: 'none',
duration: 2000
})
},
fail: function (err) {
console.log(err);
}
})
}
picHelper.getWritePhotosAlbum(callback);
break;
}
case 'bool': //正反
{
that.setData({
[url]: !that.data[url]
})
break;
}
case 'img':
case 'image': {
if (url.indexOf('qlogo') > -1) { //微信大图
url = url.replace('/132', '/0');
}
let urls = [url];
if (helper.isDefined(e.currentTarget.dataset.imgs))
urls = e.currentTarget.dataset.imgs;
wx.previewImage({
current: url, // 当前显示图片的http链接
urls
})
break;
}
default:
if (!url) return;
wx.navigateTo({
url
})
}
}
function getOptions(that, options, idName = 'id') {
let id = options[idName];
if (!id) id = options['scene']; // 二维码扫入
if (!id) return false;
that.setData({
[idName]: id
});
return true;
}
// 页面提示
function hint(msg, type = 'redirect') {
if (type == 'reLaunch')
wx.reLaunch({
url: fmtURLByPID('/pages/public/hint?type=9&msg=' + encodeURIComponent(msg)),
});
else
wx.redirectTo({
url: fmtURLByPID('/pages/public/hint?type=9&msg=' + encodeURIComponent(msg)),
});
}
// 跳转操作,找到页面中的目标,出栈后面的 delta=1为上一页面
function toURL(url) {
let pages = getCurrentPages();
for (let k = 0; k < pages.length; k++) {
if (pages[k].route.includes(url)) {
wx.navigateBack({
delta: pages.length - k - 1
});
return;
}
}
wx.redirectTo({
url,
});
}
/** ListTouch触摸开始 */
function listTouchStart(e, that) {
that.setData({
touchX: e.touches[0].pageX
})
}
/** ListTouch计算方向 */
function listTouchMove(e, that, precision = 50) {
if (that.data.touchX - e.touches[0].pageX > precision) {
that.setData({
touchDirection: 'left'
});
} else if (that.data.touchX - e.touches[0].pageX < -precision) {
that.setData({
touchDirection: 'right'
});
}
}
/** ListTouch计算滚动 */
function listTouchEnd(e, that) {
if (that.data.touchDirection == 'left') {
that.setData({
touchCur: e.currentTarget.dataset.idx
})
} else {
that.setData({
touchCur: null
})
}
that.setData({
touchDirection: null
});
}
/**
* 多条件复合查询条件
* @param {*} e
* @param {*} key 查询键值
* @param {*} val 查询值
* @param {*} def 键值的数据类型(int,str,float)
*/
function queryMulti(that, e, key, val, def) {
key = helper.isDefined(key) ? key : dataset(e, 'key');
val = helper.isDefined(val) ? val : dataset(e, 'val');
def = helper.isDefined(def) ? def : dataset(e, 'def');
// 类型转换
if (def == 'int') {
val = parseInt(val);
} else if (def == 'float') {
val = parseFloat(val);
} else if (def == 'str') {
val = val.toString();
}
let _params = that.data._params;
_params.query[key] = val;
that.setData({
_params
})
}
/**
* 页面缓存
* @param {*} key
* @param {*} that
* @param {*} listKey 数据项KEY
*/
function cacheListExist(key, that, listKey = 'list') {
key = key.toUpperCase();
if (setting.CACHE_IS_LIST)
return cacheHelper.get(key + '_LIST') && that.data && that.data[listKey];
else
return false;
}
function cacheListRemove(key) {
key = key.toUpperCase();
if (setting.CACHE_IS_LIST)
cacheHelper.remove(key + '_LIST');
}
function cacheListSet(key, time = setting.CACHE_LIST_TIME) {
key = key.toUpperCase();
if (setting.CACHE_IS_LIST)
cacheHelper.set(key + '_LIST', 'TRUE', time);
}
module.exports = {
addPhoneCalendar,
getCustomNavHeight,
getPID,
getCurrentPageURL,
getCurrentPageUrlWithArgs,
fmtURLByPID,
//### form
formClearFocus,
formHint,
//###
dataset, //节点数据data-
//### 节点操作
getPrevPage,
modifyListNode,
modifyPrevPageListNode, //单个
modifyPrevPageListNodeObject, //一组
delListNode,
delPrevPageListNode,
refreshPrevListNode,
scrollTop, //### 回顶部
// ### 图片
previewImage,
delImage,
//## 提示窗口
showSuccToastReturn,
showSuccToast,
showErrToast,
showNoneToast,
showNoneToastReturn,
showLoadingToast,
showConfirm,
showModal,
setPageData,
hint, //单独提示页
commListListener, //组件监听
bindShowModalTap,
bindHideModalTap,
showTopBtn,
getOptions, //获取id或者其他参数
model, // 双向数据绑定
switchModel, //开关控件数据绑定
top, // 回顶部事件
url, // 跳转事件
anchor, //锚点跳转事件
toURL, //跳转操作
//### 列表横向滑动
listTouchStart,
listTouchMove,
listTouchEnd,
//### 多条件复合查询
queryMulti,
clearTimer, //定时器销毁
//LIST数据缓存
cacheListExist,
cacheListRemove,
cacheListSet,
}
================================================
FILE: miniprogram/helper/pic_helper.js
================================================
/**
* Notes: 图片处理相关函数
* Ver : CCMiniCloud Framework 2.0.13 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-10-25 04:00:00
*/
function getWritePhotosAlbum(callback) {
wx.getSetting({
success: res => {
console.log('res=', res);
if (res.authSetting['scope.writePhotosAlbum']) {
console.log('true');
callback && callback();
} else if (res.authSetting['scope.writePhotosAlbum'] === undefined) {
wx.showModal({
title: '提示',
content: '您未开启保存图片到相册的权限,请点击确定去开启权限!',
success: (res) => {
if (res.confirm) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success: (res) => {
callback && callback()
console.log('授权下载成功', res);
},
fail: (res) => {
console.log('您没有授权 fail=', res);
wx.showToast({
title: '您没有授权,无法保存到相册',
icon: 'none'
});
}
});
} else {
console.log('取消了');
}
}
});
} else {
wx.showModal({
title: '提示',
content: '您未开启保存图片到相册的权限,请点击确定去开启权限!',
success: (res) => {
if (res.confirm) {
wx.openSetting({
success: (res) => {
wx.showToast({
icon: 'none',
title: '正在保存图片',
});
if (res.authSetting['scope.writePhotosAlbum']) {
console.log('false success res=', res);
callback && callback();
} else {
wx.showToast({
title: '您没有授权,无法保存到相册!',
icon: 'none'
});
}
},
fail: (res) => {
console.log('false file res=', res);
}
});
} else {
wx.showToast({
title: '您没有授权,无法保存到相册',
icon: 'none'
});
}
}
});
}
}
});
}
module.exports = {
getWritePhotosAlbum
}
================================================
FILE: miniprogram/helper/time_helper.js
================================================
/**
* Notes: 时间相关函数
* Ver : CCMiniCloud Framework 2.0.14 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-09-05 04:00:00
*/
const util = require('./helper.js');
/** 日期简化,去掉多余的前缀0 */
function simpleDate(date) {
let arr = date.split('-');
if (arr.length < 3) return date;
let month = arr[1];
if (month.indexOf('0') == 0)
month = month.replace('0', '');
let day = arr[2];
if (day.indexOf('0') == 0)
day = day.replace('0', '');
return arr[0] + '-' + month + '-' + day;
}
/** 时间格式化为年月日点分 */
function fmtDateCHN(date, fmt = 'Y-M-D') {
if (!date) return '';
if (fmt == 'hh:mm' && date.includes(':')) {
if (date.includes(' ')) date = date.split(' ')[1];
let arr = date.split(':');
return Number(arr[0]) + '点' + arr[1] + '分';
} else if (fmt == 'Y-M-D hh:mm') {
let arr = date.split(' ');
if (arr.length != 2) return date;
return fmtDateCHN(arr[0], 'Y-M-D') + fmtDateCHN(arr[1], 'hh:mm');
} else if (fmt == 'M-D hh:mm') {
let arr = date.split(' ');
if (arr.length != 2) return date;
return fmtDateCHN(arr[0], 'M-D') + ' ' + fmtDateCHN(arr[1], 'hh:mm');
} else {
if (date.includes(' ')) date = date.split(' ')[0];
let arr = date.split('-');
if (fmt == 'Y-M') //年月
return arr[0] + '年' + Number(arr[1]) + '月';
else if (fmt == 'M-D') //月日
return arr[1] + '月' + Number(arr[2]) + '日';
else if (fmt == 'Y') //年
return arr[0] + '年';
else
return arr[0] + '年' + Number(arr[1]) + '月' + Number(arr[2]) + '日';
}
}
/**
* 毫秒时间戳转时间格式
* @param {*} unixtime 毫秒
* @param {*} format Y-M-D h:m:s
* @param {*} diff 时区差异 毫秒
*/
function timestamp2Time(unixtime, format = 'Y-M-D h:m:s', diff = 0) {
let formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
let returnArr = [];
let date = new Date(unixtime + diff);
returnArr.push(date.getFullYear());
returnArr.push(formatNumber(date.getMonth() + 1));
returnArr.push(formatNumber(date.getDate()));
returnArr.push(formatNumber(date.getHours()));
returnArr.push(formatNumber(date.getMinutes()));
returnArr.push(formatNumber(date.getSeconds()));
for (let i in returnArr) {
format = format.replace(formateArr[i], returnArr[i]);
}
return format;
}
function timestame2Ago(dateTimeStamp, fmt = 'Y-M-D', diff = 0) { //dateTimeStamp是一个时间毫秒,注意时间戳是秒的形式,在这个毫秒的基础上除以1000,就是十位数的时间戳。13位数的都是时间毫秒。
let minute = 1000 * 60; //把分,时,天,周,半个月,一个月用毫秒表示
let hour = minute * 60;
let day = hour * 24;
let week = day * 7;
let month = day * 30;
let now = new Date().getTime(); //获取当前时间毫秒
let diffValue = now - dateTimeStamp; //时间差
if (diffValue < 0) {
return;
}
let minC = diffValue / minute; //计算时间差的分,时,天,周,月
let hourC = diffValue / hour;
let dayC = diffValue / day;
let result = '';
let weekC = diffValue / week;
let monthC = diffValue / month;
if (monthC >= 1 && monthC <= 3) {
result = ' ' + parseInt(monthC) + '月前'
} else if (weekC >= 1 && weekC <= 3) {
result = ' ' + parseInt(weekC) + '周前'
} else if (dayC >= 1 && dayC <= 6) {
result = ' ' + parseInt(dayC) + '天前'
} else if (hourC >= 1 && hourC <= 23) {
result = ' ' + parseInt(hourC) + '小时前'
} else if (minC >= 1 && minC <= 59) {
result = ' ' + parseInt(minC) + '分钟前'
} else if (diffValue >= 0 && diffValue <= minute) {
result = '刚刚'
} else {
result = timestamp2Time(dateTimeStamp, fmt, diff);
}
return result;
}
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
/**
* 时间转时间戳
* @param {*} date 支持 Y-M-D h:m:s / Y-M-D
*/
function time2Timestamp(date) {
if (date.length < 10) {
let arr = date.split('-');
if (arr[1].length == 1) arr[1] = '0' + arr[1];
if (arr[2].length == 1) arr[2] = '0' + arr[2];
date = arr[0] + '-' + arr[1] + '-' + arr[2];
}
if (date.length == 10) date = date + ' 00:00:00';
let d = new Date(date.replace(/-/g, '/'));
return d.getTime();
}
/**
* 获取当前时间戳/时间Y-M-D h:m:s
* @param {*} 时间格式 Y-M-D h:m:s
* @param {int} 时间步长 (秒)
*/
function time(fmt, step = 0) {
let t = 0;
if (util.isDefined(fmt)) {
let t = new Date().getTime() + step * 1000;
return timestamp2Time(t, fmt);
}
return new Date().getTime() + t * 1000;
}
// 获取某天0点
function getDayFirstTimestamp(timestamp) {
if (!timestamp) timestamp = time();
return time2Timestamp(timestamp2Time(timestamp, 'Y-M-D'));
}
/**
* 根据出生日期计算年龄周岁 传参格式为1996-06-08
* @param {*} birth
*/
function getAge(birth, isMonth = false) {
var returnAge = '';
var mouthAge = '';
var arr = birth.split('-');
var birthYear = arr[0];
var birthMonth = arr[1];
var birthDay = arr[2];
var d = new Date();
var nowYear = d.getFullYear();
var nowMonth = d.getMonth() + 1;
var nowDay = d.getDate();
if (nowYear == birthYear) {
// returnAge = 0; //同年 则为0岁
var monthDiff = nowMonth - birthMonth; //月之差
if (monthDiff < 0) { } else {
mouthAge = monthDiff + '个月';
}
} else {
var ageDiff = nowYear - birthYear; //年之差
if (ageDiff > 0) {
if (nowMonth == birthMonth) {
var dayDiff = nowDay - birthDay; //日之差
if (dayDiff < 0) {
returnAge = ageDiff - 1 + '岁';
} else {
returnAge = ageDiff + '岁';
}
} else {
var monthDiff = nowMonth - birthMonth; //月之差
if (monthDiff < 0) {
returnAge = ageDiff - 1 + '岁';
} else {
mouthAge = monthDiff + '个月';
returnAge = ageDiff + '岁';
}
}
} else {
returnAge = -1; //返回-1 表示出生日期输入错误 晚于今天
}
}
if (isMonth)
return returnAge + mouthAge; //返回周岁年龄+月份
else
return returnAge;
}
/**
* 日期计算周几
* @param {*} day 日期为输入日期,格式为 2013-03-10
*/
function week(day) {
let arys1 = new Array();
arys1 = day.split('-');
let ssdate = new Date(arys1[0], parseInt(arys1[1] - 1), arys1[2]);
let week1 = String(ssdate.getDay()).replace("0", "日").replace("1", "一").replace("2", "二").replace("3", "三").replace("4", "四").replace("5", "五").replace("6", "六") //就是你要的星期几
return "周" + week1; //就是你要的星期几
}
/** 获取某天所在某月第一天时间戳 */
function getMonthFirstTimestamp(timestamp) {
let inDate = new Date(timestamp);
let year = inDate.getFullYear();
let month = inDate.getMonth();
return new Date(year, month, 1).getTime();
}
/** 获取某天所在某月最后一天时间戳 */
function getMonthLastTimestamp(timestamp) {
let inDate = new Date(timestamp);
let year = inDate.getFullYear();
let month = inDate.getMonth();
return new Date(year, month + 1, 1).getTime() - 1;
}
// 取得分钟时间戳
function getNowMinTimestamp() {
let min = time('Y-M-D h:m') + ':00';
let timestamp = time2Timestamp(min);
return {
min,
timestamp
}
}
// 获取当前日期所在周一 输入和返回格式=yyyy-mm-dd
function getFirstOfWeek(date) {
let now = new Date(date);
let nowTime = now.getTime();
let day = now.getDay();
if (day == 0) day = 7;
let oneDayTime = 24 * 60 * 60 * 1000;
let mondayTime = nowTime - (day - 1) * oneDayTime;
return timestamp2Time(mondayTime, 'Y-M-D');
}
// 获取当前日期所在周一 输入和返回格式=yyyy-mm-dd
function getLastOfWeek(date) {
let now = new Date(date);
let nowTime = now.getTime();
let day = now.getDay();
if (day == 0) day = 7;
let oneDayTime = 24 * 60 * 60 * 1000;
let sundayTime = nowTime + (7 - day) * oneDayTime;
return timestamp2Time(sundayTime, 'Y-M-D');
}
// 获取当前日期所在月第一天 输入和返回格式=yyyy-mm-dd
function getFirstOfMonth(date) {
let arr = date.split('-');
return arr[0] + '-' + arr[1] + '-01';
}
// 获取当前日期所在月最后一天 输入和返回格式=yyyy-mm-dd
function getLastOfMonth(date) {
let now = new Date(date);
let y = now.getFullYear();
let m = now.getMonth();
let lastDay = new Date(y, m + 1, 0).getTime();
return timestamp2Time(lastDay, 'Y-M-D');
}
/**
* 取倒计时(天时分秒) 支持时间戳或者Y-M-D/Y-M-D h:m:s
* @param {*} datetimeTo
* @param {*} flag 1=正 -1=负
*/
function getTimeLeft(datetimeTo, flag = 1) {
let time1 = datetimeTo;
if (String(datetimeTo).includes('-')) {
datetimeTo = String(datetimeTo);
if (!datetimeTo.includes(':'))
datetimeTo += ' 00:00:00';
time1 = new Date(datetimeTo).getTime();
}
let time2 = new Date().getTime();
let mss = time1 - time2;
// 将时间差(毫秒)格式为:天时分秒
let days = parseInt(mss / (1000 * 60 * 60 * 24));
let hours = parseInt((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
let minutes = parseInt((mss % (1000 * 60 * 60)) / (1000 * 60));
let seconds = parseInt((mss % (1000 * 60)) / 1000);
return [flag * days, flag * hours, flag * minutes, flag * seconds];
}
module.exports = {
fmtDateCHN,
simpleDate,
getTimeLeft,
getNowMinTimestamp,
getMonthFirstTimestamp,
getMonthLastTimestamp,
getDayFirstTimestamp,
timestamp2Time,
timestame2Ago,
time2Timestamp,
time,
getAge,
week, //星期
getFirstOfWeek,
getLastOfWeek,
getFirstOfMonth,
getLastOfMonth
}
================================================
FILE: miniprogram/helper/validate.js
================================================
/**
* Notes: 数据校验类库
* Ver : CCMiniCloud Framework 2.0.15 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-01-07 07:48:00
*
*/
const pageHelper = require('./page_helper.js');
const CHECK_OPEN = true;
const CHECK_SOURCE = 'client'; //client/admin
/**
* 判断变量,参数,对象属性是否定义
* @param {*} val
*/
function isDefined(val) {
// == 不能判断是否为null
if (val === undefined)
return false;
else
return true;
}
function isNull(value) {
if (value === null || value === undefined) return true;
if (getDataType(value) == String && value === '') return true;
return false;
}
function isStrAndArrNull(value) {
if (value === null || value === undefined) return true;
let type = getDataType(value);
if (type == String && value === '') return true;
if (type == Array && value.length == 0) return true;
return false;
}
function isRealNull(value) {
if (value === null || value === undefined) return true;
let type = getDataType(value);
if (type == String && value === '') return true;
if (type == Array && value.length == 0) return true;
if (type == Object && JSON.stringify(value) == '{}') return true;
return false;
}
function getDataType(value) {
if (value === null || value === undefined) return value;
return value.constructor;
}
// 是否必填
function checkRequired(value, desc = '') {
switch (getDataType(value)) {
case Object:
if (JSON.stringify(value) == '{}')
return desc + '不能为空obj';
break;
case Array:
if (value.length == 0)
return desc + '不能为空arr';
break;
case String:
if (value.length == 0)
return desc + '不能为空';
break;
case null:
case undefined:
return desc + '不能为空';
}
}
// 校验字符/数组长度,校验数字大小
function checkMin(value, min, desc = '') {
if (isStrAndArrNull(value)) return;
min = Number(min);
switch (getDataType(value)) {
case Array:
if (value.length < min)
return desc + '不能少于' + min + '项';
break;
case String:
if (value.length < min)
return desc + '不能少于' + min + '位';
break;
case Number:
if (value < min)
return desc + '不能小于' + min;
break;
}
};
// 校验字符/数组长度,校验数字大小
function checkMax(value, max, desc = '') {
if (isStrAndArrNull(value)) return;
max = Number(max);
switch (getDataType(value)) {
case Array:
if (value.length > max)
return desc + '不能多于' + max + '项';
break;
case String:
if (value.length > max)
return desc + '不能多于' + max + '位';
break;
case Number:
if (value > max)
return desc + '不能大于' + max;
break;
}
};
// 校验字符/数组长度
function checkLen(value, len, desc = '') {
if (isStrAndArrNull(value)) return;
len = Number(len);
switch (getDataType(value)) {
case Array:
if (value.length != len)
return desc + '必须为' + len + '项';
break;
case String:
if (value.length != len)
return desc + '必须为' + len + '位';
break;
}
};
function checkMobile(value, desc = '') {
if (isNull(value)) return;
if (!/(^1[1|2|3|4|5|6|7|8|9][0-9]{9}$)/.test(value))
return desc + '格式不正确';
}
function checkInt(value, desc = '') {
if (isNull(value)) return;
if (!/^[0-9]+$/.test(value))
return desc + '必须为数字';
}
function checkDigit(value, desc = '') {
if (isNull(value)) return;
if (!/^\d+(\.\d+)?$/.test(value))
return desc + '必须为数字或小数';
}
function checkLetter(value, desc = '') {
if (isNull(value)) return;
if (!/^[A-Za-z]+$/.test(value))
return desc + '必须为字母';
}
function checkMoney(value, desc = '') {
if (isNull(value)) return;
if (!/(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/.test(value))
return desc + '必须为金额格式,例如2.00';
}
function checkLetterNum(value, desc = '') {
if (isNull(value)) return;
if (!/^\w+$/.test(value))
return desc + '必须为字母,数字和下划线';
}
function checkId(value, desc = '', min = 1, max = 100) {
if (isNull(value)) return;
min = Number(min);
max = Number(max);
if (getDataType(value) != String) return desc + '必须为ID字符串格式';
if (value.length < min || value.length > max) return desc + '必须为ID格式';
/*if (!/^\w+$/.test(value))
return desc + '必须为ID格式';*/
}
// 邮箱
function checkEmail(value, desc = '') {
if (isNull(value)) return;
let reg = /^[A-Za-z0-9+]+[A-Za-z0-9\.\_\-+]*@([A-Za-z0-9\-]+\.)+[A-Za-z0-9]+$/;
if (!reg.test(value)) return desc + '必须为邮箱格式';
}
// 短日期,形如 (yyyy-mm-dd 2008-07-22)
function checkDate(value, desc = '') {
if (isNull(value)) return;
let hint = '请选择' + desc;
if (value.length != 10) return hint;
let r = value.match(/^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/);
if (r == null) return hint;
let d = new Date(r[1], r[3] - 1, r[4]);
let chk = d.getFullYear() == r[1] && (d.getMonth() + 1) == r[3] && d.getDate() == r[4];
if (!chk) return hint;
}
// 年份,形如 (yyyy 2008)
function checkYear(value, desc = '') {
if (isNull(value)) return;
let hint = '请选择' + desc;
if (value.length != 4) return hint;
value += '-01-01';
return checkDate(value, desc);
}
// 年月,形如 (yyyy-mm 2008-01)
function checkYearMonth(value, desc = '') {
if (isNull(value)) return;
let hint = '请选择' + desc;
if (value.length != 7) return hint;
value += '-01';
return checkDate(value, desc);
}
// 短时间(时分秒),形如 (13:04:06)
function checkTime(value, desc = '') {
if (isNull(value)) return;
let hint = desc + '必须为时间格式';
if (value.length != 8) return hint;
let a = value.match(/^(\d{1,2})(:)?(\d{1,2})\2(\d{1,2})$/);
if (a == null) return hint;
if (a[1] > 23 || a[3] > 59 || a[4] > 59) return hint;
}
// 短时间(时分),形如 (hh:mm 13:04)
function checkHourMinute(value, desc = '') {
if (isNull(value)) return;
let hint = desc + '必须为时分时间格式';
if (value.length != 5) return hint;
value += ':01';
return checkTime(value, desc);
}
// 长时间,形如 (2008-07-22 13:04:06)
function checkDatimeTime(value, desc = '') {
if (isNull(value)) return;
let hint = desc + '必须为完整时间格式';
if (value.length != 19) return hint;
var reg = /^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/;
var r = value.match(reg);
if (r == null) return hint;
var d = new Date(r[1], r[3] - 1, r[4], r[5], r[6], r[7]);
let chk = d.getFullYear() == r[1] && (d.getMonth() + 1) == r[3] && d.getDate() == r[4] && d.getHours() == r[5] && d.getMinutes() == r[6] && d.getSeconds() == r[7];
if (!chk) return hint;
}
function checkArray(value, desc = '') {
if (!Array.isArray(value))
return desc + '填写错误arr';
}
function checkObject(value, desc = '') {
if (value.constructor != Object)
return desc + '填写错误obj';
}
function checkBoolean(value, desc = '') {
if (value.constructor != Boolean)
return desc + '填写错误bool';
}
// 枚举 ref=1,2,3,4格式
function checkIn(value, ref, desc = '') {
if (isNull(value)) return;
let type = getDataType(value);
if (type != String && type != Number) return desc + '填写范围错误';
let arr = String(ref).split(',');
if (!arr.includes(value) && !arr.includes(value + ''))
return desc + '填写范围错误';
}
function checkIds(value, desc) {}
function checkString(value, desc) {
if (value.constructor != String)
return desc + '填写错误';
}
function check(data, rules, that) {
let returnData = {};
for (let key in rules) {
let arr = rules[key].split('|');
let desc = key; // 字段说明
let defVal = undefined; // 缺省值
let dataType = 'String'; //数据类型
if (!CHECK_OPEN) { // 不校验
// 取值
let val = data[formName];
returnData[key] = val;
continue;
}
// 小循环获取规则
for (let i = 0; i < arr.length; i++) {
// 数据项说明
if (arr[i].startsWith('name=')) {
desc = '「' + arr[i].replace('name=', '') + '」';
continue;
}
// 缺省值
if (arr[i].startsWith('default=')) {
defVal = arr[i].replace('default=', '').trim();
continue;
}
// 数据类型
switch (arr[i].toLowerCase()) {
case 'int':
case 'digit':
dataType = 'Number';
break;
case 'array':
case 'arr':
dataType = 'Array';
break;
case 'object':
case 'obj':
dataType = 'Object';
break;
case 'bool':
case 'boolean':
dataType = 'Boolean';
break;
}
}
// 校验
let formName = (CHECK_SOURCE == 'admin') ? k : arr[0]; // 表单名 admin/client
// 取值
let val = data[formName];
switch (dataType) {
case 'Array': {
if (defVal !== undefined) {
try {
defVal = JSON.parse(defVal);
if (getDataType(defVal) != Array)
return _showError(desc + '默认值数组格式错误', formName, that);
} catch (ex) {
return _showError(desc + '默认值数组格式错误', formName, that);
}
}
if (val === null || val === undefined) val = defVal;
if (val !== undefined && getDataType(val) != Array)
return _showError(desc + '数组格式错误', formName, that);
break;
}
case 'Object': {
if (defVal !== undefined) {
try {
defVal = JSON.parse(defVal);
if (getDataType(defVal) != Object)
return _showError(desc + '默认值对象格式错误', formName, that);
} catch (ex) {
return _showError(desc + '默认值对象格式错误', formName, that);
}
}
if (val === null || val === undefined) val = defVal;
if (val !== undefined && getDataType(val) != Object)
return _showError(desc + '对象格式错误', formName, that);
break;
}
case 'Boolean': {
if (defVal !== undefined) {
try {
defVal = JSON.parse(defVal);
if (getDataType(defVal) != Boolean)
return _showError(desc + '默认值布尔格式错误', formName, that);
} catch (ex) {
return _showError(desc + '默认值布尔格式错误');
}
}
if (val === null || val === undefined) val = defVal;
if (val !== undefined && getDataType(val) != Boolean)
return _showError(desc + '布尔格式错误', formName, that);
break;
}
case 'Number': {
if (checkDigit(defVal, desc + '默认值'))
return _showError(desc + '默认值格式错误', formName, that);
if (val === null || val === undefined) val = defVal;
if (val === undefined) break;
if (val === '') //数字不能为空
return _showError(desc + '不能为空', formName, that);
let dataType = getDataType(val);
if (dataType == Object || dataType == Boolean || dataType == Array)
return _showError(desc + '必须为数字格式', formName, that);
// 数字格式校验
let result = checkDigit(val, desc);
if (result) return _showError(result, formName, that);
val = Number(val);
break;
}
case 'String': {
let dataType = getDataType(val);
if (dataType == Object || dataType == Boolean || dataType == Array)
return _showError(desc + '必须为字符串格式', formName, that);
if (val === null || val === undefined) val = defVal;
if (val === undefined) break;
try {
val = String(val).trim(); // 数字会被转为字符串
} catch (ex) {
return _showError(desc + '必须为字符串格式', formName, that);
}
break;
}
}
returnData[key] = val;
let fromStep = (CHECK_SOURCE == 'admin') ? 0 : 1; //admin/client
for (let i = fromStep; i < arr.length; i++) {
let result = '';
let rules = arr[i].split(':');
let ruleName = rules[0];
// 空 且非必填的 不校验
if (ruleName != 'must' && val === undefined) continue;
switch (ruleName) {
case 'must':
result = checkRequired(val, desc);
break;
case 'str':
case 'string':
result = checkString(val, desc);
break;
case 'arr':
case 'array':
result = checkArray(val, desc);
break;
case 'obj':
case 'object':
result = checkObject(val, desc);
break;
case 'bool':
case 'boolean':
result = checkBoolean(val, desc);
break;
case 'money':
result = checkMoney(val, desc);
break;
case 'year':
result = checkYear(val, desc);
break;
case 'yearmonth':
result = checkYearMonth(val, desc);
break;
case 'date':
result = checkDate(val, desc);
break;
case 'time':
result = checkTime(val, desc);
break;
case 'hourminute':
result = checkHourMinute(val, desc);
break;
case 'datetime':
result = checkDatimeTime(val, desc);
break;
case 'min':
result = checkMin(val, Number(rules[1]), desc);
break;
case 'max':
result = checkMax(val, Number(rules[1]), desc);
break;
case 'len':
result = checkLen(val, Number(rules[1]), desc);
break;
case 'in':
result = checkIn(val, rules[1], desc);
break;
case 'email':
result = checkEmail(val, desc);
break;
case 'mobile':
result = checkMobile(val, desc);
break;
case 'int': // 正整数
result = checkInt(val, desc);
break;
case 'digit': // 正小整数
result = checkDigit(val, desc);
break;
case 'id':
result = checkId(val, desc);
break;
case 'letter':
result = checkLetter(val, desc);
break;
case 'letter_num':
result = checkLetterNum(val, desc);
break;
}
if (result) {
_showError(result, formName, that);
return false;
} else {
if (that) {
if (CHECK_SOURCE == 'client') {
// 删除原有的自动聚焦 //admin/client
if (isDefined(that.data[formName + 'Focus'])) {
that.setData({ //TODO delete?
[formName + 'Focus']: false
});
}
}
}
}
}
}
return returnData;
}
function _showError(result, formName, that) { //admin/client
if (CHECK_SOURCE == 'client') {
wx.showModal({
title: '温馨提示',
content: result,
showCancel: false,
success(res) {
// 自动聚焦
if (that) {
pageHelper.anchor(formName, that);
that.setData({
[formName + 'Focus']: result,
});
}
}
});
} else {
throw new AppError(result, appCode.DATA);
}
}
module.exports = {
check,
checkString,
checkArray,
checkObject,
checkMoney,
checkYear,
checkYearMonth,
checkDate,
checkTime,
checkHourMinute,
checkDatimeTime,
checkMin,
checkMax,
checkLen,
checkIn,
checkEmail,
checkMobile,
checkInt, // 正小整数
checkDigit,
checkId,
checkLetter,
checkLetterNum,
}
================================================
FILE: miniprogram/lib/tools/base64_lib.js
================================================
/**
* Notes:
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-10-25 04:00:00
*/
function Base64() {
// private property
var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
var _utf8_encode = function (string) {
string = string.replace(/\r\n/g, "\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if ((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
var _utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i + 1);
c3 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
var base = new Base64();
function encode(str) {
return base.encode(str);
}
function decode(str) {
return base.decode(str);
}
function safeEncode(str) {
return encode(str).replace(/[\+=\/]/g, function (c) {
switch (c) {
case '+':
return '-';
case '=':
return '';
case '/':
return '_';
}
})
}
export {
encode,
decode,
safeEncode
}
================================================
FILE: miniprogram/lib/tools/lunar_lib.js
================================================
/* 公历转农历代码思路:
1、建立农历年份查询表
2、计算输入公历日期与公历基准的相差天数
3、从农历基准开始遍历农历查询表,计算自农历基准之后每一年的天数,并用相差天数依次相减,确定农历年份
4、利用剩余相差天数以及农历每个月的天数确定农历月份
5、利用剩余相差天数确定农历哪一天
https://github.com/xm2by/fragment
*/
//农历节日
const LUNAR_HOLIDAY = {
'0101': '春节',
'0115': '元宵节',
'0505': '端午节',
'0707': '七夕',
'0715': '中元节',
'0815': '中秋节',
'0909': '重阳节',
'1208': '腊八节',
'1224': '小年'
};
// 公历节日
const PUBLIC_HOLIDAY = {
'0101': '元旦',
'0214': '情人节',
'0307': '女生节',
'0308': '妇女节',
'0312': '植树节',
'0314': '白色情人',
'0315': '消费者日',
'0401': '愚人节',
'0404': '复活节',
'0501': '劳动节',
'0504': '青年节',
'0510': '母亲节',
'0512': '护士节',
'0601': '儿童节',
'0620': '父亲节',
'0701': '建党节',
'0801': '建军节',
'0910': '教师节',
'0928': '孔子诞辰',
'1001': '国庆节',
'1006': '老人节',
'1024': '联合国日',
'1101': '万圣节',
'1125': '感恩节',
'1224': '平安夜',
'1225': '圣诞节'
};
const SOLAR_STERM = ['小寒', '大寒', '立春', '雨水', '惊蛰', '春分', '清明', '谷雨', '立夏', '小满', '芒种', '夏至', '小暑', '大暑', '立秋', '处暑', '白露', '秋分', '寒露', '霜降', '立冬', '小雪', '大雪', '冬至'];
const SOLAR_STERM_INFO = [0, 1272480000, 2548020000, 3830160000, 5120220000, 6420840000,
7732020000, 9055260000, 10388940000, 11733060000, 13084320000, 14441580000,
15800580000, 17159340000, 18513780000, 19861980000, 21201000000, 22529640000,
23846820000, 25152600000, 26447700000, 27733440000, 29011920000, 30285480000
];
// 农历1949-2100年查询表
const LUNAR_YEAR_ARR = [
0x0b557, //1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, //1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, //1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, //1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, //1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, //1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, //2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, //2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, //2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, //2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, //2040-2049
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, //2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, //2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, //2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, //2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, //2090-2099
0x0d520 //2100
];
const LUNAR_MONTH = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
const LUNAR_DAY = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '初', '廿'];
const TIAN_GAN = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'];
const DIZHI = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
// 公历转农历函数
function sloarToLunar(sy, sm, sd) {
if (typeof (sy) == 'string') {
let arr = sy.split('-');
sy = Number(arr[0]);
sm = Number(arr[1]);
sd = Number(arr[2]);
}
// 公历节日
let publicHoliday = (sm > 9 ? sm : '0' + sm) + '' + (sd > 9 ? sd : '0' + sd);
if (PUBLIC_HOLIDAY.hasOwnProperty(publicHoliday)) {
return PUBLIC_HOLIDAY[publicHoliday];
}
// 输入的月份减1处理
sm -= 1;
// 计算与公历基准的相差天数
// Date.UTC()返回的是距离公历1970年1月1日的毫秒数,传入的月份需要减1
let daySpan = (Date.UTC(sy, sm, sd) - Date.UTC(1949, 0, 29)) / (24 * 60 * 60 * 1000) + 1;
let ly, lm, ld;
// 确定输出的农历年份
for (let j = 0; j < LUNAR_YEAR_ARR.length; j++) {
daySpan -= lunarYearDays(LUNAR_YEAR_ARR[j]);
if (daySpan <= 0) {
ly = 1949 + j;
// 获取农历年份确定后的剩余天数
daySpan += lunarYearDays(LUNAR_YEAR_ARR[j]);
break
}
}
// 确定输出的农历月份
for (let k = 0; k < lunarYearMonths(LUNAR_YEAR_ARR[ly - 1949]).length; k++) {
daySpan -= lunarYearMonths(LUNAR_YEAR_ARR[ly - 1949])[k];
if (daySpan <= 0) {
// 有闰月时,月份的数组长度会变成13,因此,当闰月月份小于等于k时,lm不需要加1
if (hasLeapMonth(LUNAR_YEAR_ARR[ly - 1949]) && hasLeapMonth(LUNAR_YEAR_ARR[ly - 1949]) <= k) {
if (hasLeapMonth(LUNAR_YEAR_ARR[ly - 1949]) < k) {
lm = k;
} else if (hasLeapMonth(LUNAR_YEAR_ARR[ly - 1949]) === k) {
lm = '闰' + k;
} else {
lm = k + 1;
}
} else {
lm = k + 1;
}
// 获取农历月份确定后的剩余天数
daySpan += lunarYearMonths(LUNAR_YEAR_ARR[ly - 1949])[k];
break
}
}
// 确定输出农历哪一天
ld = daySpan;
//农历节日
let paraHoliday = (lm > 9 ? lm : '0' + lm) + '' + (ld > 9 ? ld : '0' + ld);
if (LUNAR_HOLIDAY.hasOwnProperty(paraHoliday)) {
return LUNAR_HOLIDAY[paraHoliday];
}
// TODO:除夕
/*
if (lm == 12) {
var theLastDay = lDate.isLeap ? leapDays(y) : monthDays(y, m); //农历当月最後一天
if (theLastDay == ld) {
return "除夕";
}
}*/
// 农历节气
let temp = getSolarTerm(sy, sm + 1, sd)
if (temp) return temp;
// 将计算出来的农历月份转换成汉字月份,闰月需要在前面加上闰字
if (hasLeapMonth(LUNAR_YEAR_ARR[ly - 1949]) && (typeof (lm) === 'string' && lm.indexOf('闰') > -1)) {
lm = `闰${LUNAR_MONTH[/\d/.exec(lm) - 1]}`
} else {
lm = LUNAR_MONTH[lm - 1];
}
// 将计算出来的农历年份转换为天干地支年
//ly = getTianGan(ly) + getDiZhi(ly);
ly = '';
// 将计算出来的农历天数转换成汉字
if (ld < 11) {
ld = `${LUNAR_DAY[10]}${LUNAR_DAY[ld-1]}`
} else if (ld > 10 && ld < 20) {
ld = `${LUNAR_DAY[9]}${LUNAR_DAY[ld-11]}`
} else if (ld === 20) {
ld = `${LUNAR_DAY[1]}${LUNAR_DAY[9]}`
} else if (ld > 20 && ld < 30) {
ld = `${LUNAR_DAY[11]}${LUNAR_DAY[ld-21]}`
} else if (ld === 30) {
ld = `${LUNAR_DAY[2]}${LUNAR_DAY[9]}`
}
//console.log(ly, lm, ld);
if (ld == '初一') ld = lm + '月';
/*
return {
lunarYear: ly,
LUNAR_MONTH: lm,
LUNAR_DAY: ld,
}*/
return ld;
}
// 计算农历年是否有闰月,参数为存储农历年的16进制
// 农历年份信息用16进制存储,其中16进制的最后1位可以用于判断是否有闰月
function hasLeapMonth(ly) {
// 获取16进制的最后1位,需要用到&与运算符
if (ly & 0xf) {
return ly & 0xf
} else {
return false
}
}
// 如果有闰月,计算农历闰月天数,参数为存储农历年的16进制
// 农历年份信息用16进制存储,其中16进制的第1位(0x除外)可以用于表示闰月是大月还是小月
function leapMonthDays(ly) {
if (hasLeapMonth(ly)) {
// 获取16进制的第1位(0x除外)
return (ly & 0xf0000) ? 30 : 29
} else {
return 0
}
}
// 计算农历一年的总天数,参数为存储农历年的16进制
// 农历年份信息用16进制存储,其中16进制的第2-4位(0x除外)可以用于表示正常月是大月还是小月
function lunarYearDays(ly) {
let totalDays = 0;
// 获取正常月的天数,并累加
// 获取16进制的第2-4位,需要用到>>移位运算符
for (let i = 0x8000; i > 0x8; i >>= 1) {
let monthDays = (ly & i) ? 30 : 29;
totalDays += monthDays;
}
// 如果有闰月,需要把闰月的天数加上
if (hasLeapMonth(ly)) {
totalDays += leapMonthDays(ly);
}
return totalDays
}
// 获取农历每个月的天数
// 参数需传入16进制数值
function lunarYearMonths(ly) {
let monthArr = [];
// 获取正常月的天数,并添加到monthArr数组中
// 获取16进制的第2-4位,需要用到>>移位运算符
for (let i = 0x8000; i > 0x8; i >>= 1) {
monthArr.push((ly & i) ? 30 : 29);
}
// 如果有闰月,需要把闰月的天数加上
if (hasLeapMonth(ly)) {
monthArr.splice(hasLeapMonth(ly), 0, leapMonthDays(ly));
}
return monthArr
}
// 将农历年转换为天干,参数为农历年
function getTianGan(ly) {
let tianGanKey = (ly - 3) % 10;
if (tianGanKey === 0) tianGanKey = 10;
return TIAN_GAN[tianGanKey - 1]
}
// 将农历年转换为地支,参数为农历年
function getDiZhi(ly) {
let diZhiKey = (ly - 3) % 12;
if (diZhiKey === 0) diZhiKey = 12;
return DIZHI[diZhiKey - 1]
}
/**
* 节气(参数为公历)
* @param {*} sy
* @param {*} sm
* @param {*} sd
*/
function getSolarTerm(sy, sm, sd) {
sm -= 1;
let solarTermStr = "";
//月份乘2是因为每月平均2节气对应二十四节气加一考虑存在闰月
let tmp1 = new Date((31556925974.7 * (sy - 1900) + SOLAR_STERM_INFO[sm * 2 + 1]) + Date.UTC(1900, 0, 6, 2, 5));
let tmp2 = tmp1.getUTCDate();
if (tmp2 == sd) solarTermStr = SOLAR_STERM[sm * 2 + 1];
tmp1 = new Date((31556925974.7 * (sy - 1900) + SOLAR_STERM_INFO[sm * 2]) + Date.UTC(1900, 0, 6, 2, 5));
tmp2 = tmp1.getUTCDate();
if (tmp2 == sd) solarTermStr = SOLAR_STERM[sm * 2];
if (sd > 1) {
sd -= 1;
} else {
sm -= 1;
sd = 31;
if (sm < 0) {
sy -= 1;
sm = 11;
}
}
return solarTermStr;
}
module.exports = {
sloarToLunar
}
================================================
FILE: miniprogram/lib/tools/qrcode_lib.js
================================================
//---------------------------------------------------------------------
/*
* @demoURL: https://github.com/Pudon/weapp-qrcode
*/
//---------------------------------------------------------------------
/**
* qrcode
* @param typeNumber 1 to 40 Version
* @param errorCorrectLevel 'L','M','Q','H'
*/
var qrcode = function(typeNumber, errorCorrectLevel) {
var PAD0 = 0xEC;
var PAD1 = 0x11;
var _typeNumber = typeNumber;
var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel];
var _modules = null;
var _moduleCount = 0;
var _dataCache = null;
var _dataList = new Array();
var _this = {};
var makeImpl = function(test, maskPattern) {
_moduleCount = _typeNumber * 4 + 17;
_modules = function(moduleCount) {
var modules = new Array(moduleCount);
for (var row = 0; row < moduleCount; row += 1) {
modules[row] = new Array(moduleCount);
for (var col = 0; col < moduleCount; col += 1) {
modules[row][col] = null;
}
}
return modules;
}(_moduleCount);
setupPositionProbePattern(0, 0);
setupPositionProbePattern(_moduleCount - 7, 0);
setupPositionProbePattern(0, _moduleCount - 7);
setupPositionAdjustPattern();
setupTimingPattern();
setupTypeInfo(test, maskPattern);
if (_typeNumber >= 7) {
setupTypeNumber(test);
}
if (_dataCache == null) {
_dataCache = createData(_typeNumber, _errorCorrectLevel, _dataList);
}
mapData(_dataCache, maskPattern);
};
var setupPositionProbePattern = function(row, col) {
for (var r = -1; r <= 7; r += 1) {
if (row + r <= -1 || _moduleCount <= row + r) continue;
for (var c = -1; c <= 7; c += 1) {
if (col + c <= -1 || _moduleCount <= col + c) continue;
if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
|| (0 <= c && c <= 6 && (r == 0 || r == 6) )
|| (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
_modules[row + r][col + c] = true;
} else {
_modules[row + r][col + c] = false;
}
}
}
};
var getBestMaskPattern = function() {
var minLostPoint = 0;
var pattern = 0;
for (var i = 0; i < 8; i += 1) {
makeImpl(true, i);
var lostPoint = QRUtil.getLostPoint(_this);
if (i == 0 || minLostPoint > lostPoint) {
minLostPoint = lostPoint;
pattern = i;
}
}
return pattern;
};
var setupTimingPattern = function() {
for (var r = 8; r < _moduleCount - 8; r += 1) {
if (_modules[r][6] != null) {
continue;
}
_modules[r][6] = (r % 2 == 0);
}
for (var c = 8; c < _moduleCount - 8; c += 1) {
if (_modules[6][c] != null) {
continue;
}
_modules[6][c] = (c % 2 == 0);
}
};
var setupPositionAdjustPattern = function() {
var pos = QRUtil.getPatternPosition(_typeNumber);
for (var i = 0; i < pos.length; i += 1) {
for (var j = 0; j < pos.length; j += 1) {
var row = pos[i];
var col = pos[j];
if (_modules[row][col] != null) {
continue;
}
for (var r = -2; r <= 2; r += 1) {
for (var c = -2; c <= 2; c += 1) {
if (r == -2 || r == 2 || c == -2 || c == 2
|| (r == 0 && c == 0) ) {
_modules[row + r][col + c] = true;
} else {
_modules[row + r][col + c] = false;
}
}
}
}
}
};
var setupTypeNumber = function(test) {
var bits = QRUtil.getBCHTypeNumber(_typeNumber);
for (var i = 0; i < 18; i += 1) {
var mod = (!test && ( (bits >> i) & 1) == 1);
_modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
}
for (var i = 0; i < 18; i += 1) {
var mod = (!test && ( (bits >> i) & 1) == 1);
_modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
}
};
var setupTypeInfo = function(test, maskPattern) {
var data = (_errorCorrectLevel << 3) | maskPattern;
var bits = QRUtil.getBCHTypeInfo(data);
// vertical
for (var i = 0; i < 15; i += 1) {
var mod = (!test && ( (bits >> i) & 1) == 1);
if (i < 6) {
_modules[i][8] = mod;
} else if (i < 8) {
_modules[i + 1][8] = mod;
} else {
_modules[_moduleCount - 15 + i][8] = mod;
}
}
// horizontal
for (var i = 0; i < 15; i += 1) {
var mod = (!test && ( (bits >> i) & 1) == 1);
if (i < 8) {
_modules[8][_moduleCount - i - 1] = mod;
} else if (i < 9) {
_modules[8][15 - i - 1 + 1] = mod;
} else {
_modules[8][15 - i - 1] = mod;
}
}
// fixed module
_modules[_moduleCount - 8][8] = (!test);
};
var mapData = function(data, maskPattern) {
var inc = -1;
var row = _moduleCount - 1;
var bitIndex = 7;
var byteIndex = 0;
var maskFunc = QRUtil.getMaskFunction(maskPattern);
for (var col = _moduleCount - 1; col > 0; col -= 2) {
if (col == 6) col -= 1;
while (true) {
for (var c = 0; c < 2; c += 1) {
if (_modules[row][col - c] == null) {
var dark = false;
if (byteIndex < data.length) {
dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
}
var mask = maskFunc(row, col - c);
if (mask) {
dark = !dark;
}
_modules[row][col - c] = dark;
bitIndex -= 1;
if (bitIndex == -1) {
byteIndex += 1;
bitIndex = 7;
}
}
}
row += inc;
if (row < 0 || _moduleCount <= row) {
row -= inc;
inc = -inc;
break;
}
}
}
};
var createBytes = function(buffer, rsBlocks) {
var offset = 0;
var maxDcCount = 0;
var maxEcCount = 0;
var dcdata = new Array(rsBlocks.length);
var ecdata = new Array(rsBlocks.length);
for (var r = 0; r < rsBlocks.length; r += 1) {
var dcCount = rsBlocks[r].dataCount;
var ecCount = rsBlocks[r].totalCount - dcCount;
maxDcCount = Math.max(maxDcCount, dcCount);
maxEcCount = Math.max(maxEcCount, ecCount);
dcdata[r] = new Array(dcCount);
for (var i = 0; i < dcdata[r].length; i += 1) {
dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
}
offset += dcCount;
var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);
var modPoly = rawPoly.mod(rsPoly);
ecdata[r] = new Array(rsPoly.getLength() - 1);
for (var i = 0; i < ecdata[r].length; i += 1) {
var modIndex = i + modPoly.getLength() - ecdata[r].length;
ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0;
}
}
var totalCodeCount = 0;
for (var i = 0; i < rsBlocks.length; i += 1) {
totalCodeCount += rsBlocks[i].totalCount;
}
var data = new Array(totalCodeCount);
var index = 0;
for (var i = 0; i < maxDcCount; i += 1) {
for (var r = 0; r < rsBlocks.length; r += 1) {
if (i < dcdata[r].length) {
data[index] = dcdata[r][i];
index += 1;
}
}
}
for (var i = 0; i < maxEcCount; i += 1) {
for (var r = 0; r < rsBlocks.length; r += 1) {
if (i < ecdata[r].length) {
data[index] = ecdata[r][i];
index += 1;
}
}
}
return data;
};
var createData = function(typeNumber, errorCorrectLevel, dataList) {
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
var buffer = qrBitBuffer();
for (var i = 0; i < dataList.length; i += 1) {
var data = dataList[i];
buffer.put(data.getMode(), 4);
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
data.write(buffer);
}
// calc num max data.
var totalDataCount = 0;
for (var i = 0; i < rsBlocks.length; i += 1) {
totalDataCount += rsBlocks[i].dataCount;
}
if (buffer.getLengthInBits() > totalDataCount * 8) {
throw new Error('code length overflow. ('
+ buffer.getLengthInBits()
+ '>'
+ totalDataCount * 8
+ ')');
}
// end code
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
buffer.put(0, 4);
}
// padding
while (buffer.getLengthInBits() % 8 != 0) {
buffer.putBit(false);
}
// padding
while (true) {
if (buffer.getLengthInBits() >= totalDataCount * 8) {
break;
}
buffer.put(PAD0, 8);
if (buffer.getLengthInBits() >= totalDataCount * 8) {
break;
}
buffer.put(PAD1, 8);
}
return createBytes(buffer, rsBlocks);
};
_this.addData = function(data) {
var newData = qr8BitByte(data);
_dataList.push(newData);
_dataCache = null;
};
_this.isDark = function(row, col) {
if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
throw new Error(row + ',' + col);
}
return _modules[row][col];
};
_this.getModuleCount = function() {
return _moduleCount;
};
_this.make = function() {
makeImpl(false, getBestMaskPattern() );
};
_this.createTableTag = function(cellSize, margin) {
cellSize = cellSize || 2;
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
var qrHtml = '';
qrHtml += '
';
qrHtml += '';
for (var r = 0; r < _this.getModuleCount(); r += 1) {
qrHtml += '';
for (var c = 0; c < _this.getModuleCount(); c += 1) {
qrHtml += ' | ';
}
qrHtml += '
';
}
qrHtml += '';
qrHtml += '
';
return qrHtml;
};
_this.createImgTag = function(cellSize, margin, size) {
cellSize = cellSize || 2;
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
var min = margin;
var max = _this.getModuleCount() * cellSize + margin;
return createImgTag(size, size, function(x, y) {
if (min <= x && x < max && min <= y && y < max) {
var c = Math.floor( (x - min) / cellSize);
var r = Math.floor( (y - min) / cellSize);
return _this.isDark(r, c)? 0 : 1;
} else {
return 1;
}
} );
};
return _this;
};
//---------------------------------------------------------------------
// qrcode.stringToBytes
//---------------------------------------------------------------------
qrcode.stringToBytes = function(s) {
var bytes = new Array();
for (var i = 0; i < s.length; i += 1) {
var c = s.charCodeAt(i);
bytes.push(c & 0xff);
}
return bytes;
};
//---------------------------------------------------------------------
// qrcode.createStringToBytes
//---------------------------------------------------------------------
/**
* @param unicodeData base64 string of byte array.
* [16bit Unicode],[16bit Bytes], ...
* @param numChars
*/
qrcode.createStringToBytes = function(unicodeData, numChars) {
// create conversion map.
var unicodeMap = function() {
var bin = base64DecodeInputStream(unicodeData);
var read = function() {
var b = bin.read();
if (b == -1) throw new Error();
return b;
};
var count = 0;
var unicodeMap = {};
while (true) {
var b0 = bin.read();
if (b0 == -1) break;
var b1 = read();
var b2 = read();
var b3 = read();
var k = String.fromCharCode( (b0 << 8) | b1);
var v = (b2 << 8) | b3;
unicodeMap[k] = v;
count += 1;
}
if (count != numChars) {
throw new Error(count + ' != ' + numChars);
}
return unicodeMap;
}();
var unknownChar = '?'.charCodeAt(0);
return function(s) {
var bytes = new Array();
for (var i = 0; i < s.length; i += 1) {
var c = s.charCodeAt(i);
if (c < 128) {
bytes.push(c);
} else {
var b = unicodeMap[s.charAt(i)];
if (typeof b == 'number') {
if ( (b & 0xff) == b) {
// 1byte
bytes.push(b);
} else {
// 2bytes
bytes.push(b >>> 8);
bytes.push(b & 0xff);
}
} else {
bytes.push(unknownChar);
}
}
}
return bytes;
};
};
//---------------------------------------------------------------------
// QRMode
//---------------------------------------------------------------------
var QRMode = {
MODE_NUMBER : 1 << 0,
MODE_ALPHA_NUM : 1 << 1,
MODE_8BIT_BYTE : 1 << 2,
MODE_KANJI : 1 << 3
};
//---------------------------------------------------------------------
// QRErrorCorrectLevel
//---------------------------------------------------------------------
var QRErrorCorrectLevel = {
L : 1,
M : 0,
Q : 3,
H : 2
};
//---------------------------------------------------------------------
// QRMaskPattern
//---------------------------------------------------------------------
var QRMaskPattern = {
PATTERN000 : 0,
PATTERN001 : 1,
PATTERN010 : 2,
PATTERN011 : 3,
PATTERN100 : 4,
PATTERN101 : 5,
PATTERN110 : 6,
PATTERN111 : 7
};
//---------------------------------------------------------------------
// QRUtil
//---------------------------------------------------------------------
var QRUtil = function() {
var PATTERN_POSITION_TABLE = [
[],
[6, 18],
[6, 22],
[6, 26],
[6, 30],
[6, 34],
[6, 22, 38],
[6, 24, 42],
[6, 26, 46],
[6, 28, 50],
[6, 30, 54],
[6, 32, 58],
[6, 34, 62],
[6, 26, 46, 66],
[6, 26, 48, 70],
[6, 26, 50, 74],
[6, 30, 54, 78],
[6, 30, 56, 82],
[6, 30, 58, 86],
[6, 34, 62, 90],
[6, 28, 50, 72, 94],
[6, 26, 50, 74, 98],
[6, 30, 54, 78, 102],
[6, 28, 54, 80, 106],
[6, 32, 58, 84, 110],
[6, 30, 58, 86, 114],
[6, 34, 62, 90, 118],
[6, 26, 50, 74, 98, 122],
[6, 30, 54, 78, 102, 126],
[6, 26, 52, 78, 104, 130],
[6, 30, 56, 82, 108, 134],
[6, 34, 60, 86, 112, 138],
[6, 30, 58, 86, 114, 142],
[6, 34, 62, 90, 118, 146],
[6, 30, 54, 78, 102, 126, 150],
[6, 24, 50, 76, 102, 128, 154],
[6, 28, 54, 80, 106, 132, 158],
[6, 32, 58, 84, 110, 136, 162],
[6, 26, 54, 82, 110, 138, 166],
[6, 30, 58, 86, 114, 142, 170]
];
var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0);
var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1);
var _this = {};
var getBCHDigit = function(data) {
var digit = 0;
while (data != 0) {
digit += 1;
data >>>= 1;
}
return digit;
};
_this.getBCHTypeInfo = function(data) {
var d = data << 10;
while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) );
}
return ( (data << 10) | d) ^ G15_MASK;
};
_this.getBCHTypeNumber = function(data) {
var d = data << 12;
while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) );
}
return (data << 12) | d;
};
_this.getPatternPosition = function(typeNumber) {
return PATTERN_POSITION_TABLE[typeNumber - 1];
};
_this.getMaskFunction = function(maskPattern) {
switch (maskPattern) {
case QRMaskPattern.PATTERN000 :
return function(i, j) { return (i + j) % 2 == 0; };
case QRMaskPattern.PATTERN001 :
return function(i, j) { return i % 2 == 0; };
case QRMaskPattern.PATTERN010 :
return function(i, j) { return j % 3 == 0; };
case QRMaskPattern.PATTERN011 :
return function(i, j) { return (i + j) % 3 == 0; };
case QRMaskPattern.PATTERN100 :
return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; };
case QRMaskPattern.PATTERN101 :
return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; };
case QRMaskPattern.PATTERN110 :
return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; };
case QRMaskPattern.PATTERN111 :
return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; };
default :
throw new Error('bad maskPattern:' + maskPattern);
}
};
_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
var a = qrPolynomial([1], 0);
for (var i = 0; i < errorCorrectLength; i += 1) {
a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) );
}
return a;
};
_this.getLengthInBits = function(mode, type) {
if (1 <= type && type < 10) {
// 1 - 9
switch(mode) {
case QRMode.MODE_NUMBER : return 10;
case QRMode.MODE_ALPHA_NUM : return 9;
case QRMode.MODE_8BIT_BYTE : return 8;
case QRMode.MODE_KANJI : return 8;
default :
throw new Error('mode:' + mode);
}
} else if (type < 27) {
// 10 - 26
switch(mode) {
case QRMode.MODE_NUMBER : return 12;
case QRMode.MODE_ALPHA_NUM : return 11;
case QRMode.MODE_8BIT_BYTE : return 16;
case QRMode.MODE_KANJI : return 10;
default :
throw new Error('mode:' + mode);
}
} else if (type < 41) {
// 27 - 40
switch(mode) {
case QRMode.MODE_NUMBER : return 14;
case QRMode.MODE_ALPHA_NUM : return 13;
case QRMode.MODE_8BIT_BYTE : return 16;
case QRMode.MODE_KANJI : return 12;
default :
throw new Error('mode:' + mode);
}
} else {
throw new Error('type:' + type);
}
};
_this.getLostPoint = function(qrcode) {
var moduleCount = qrcode.getModuleCount();
var lostPoint = 0;
// LEVEL1
for (var row = 0; row < moduleCount; row += 1) {
for (var col = 0; col < moduleCount; col += 1) {
var sameCount = 0;
var dark = qrcode.isDark(row, col);
for (var r = -1; r <= 1; r += 1) {
if (row + r < 0 || moduleCount <= row + r) {
continue;
}
for (var c = -1; c <= 1; c += 1) {
if (col + c < 0 || moduleCount <= col + c) {
continue;
}
if (r == 0 && c == 0) {
continue;
}
if (dark == qrcode.isDark(row + r, col + c) ) {
sameCount += 1;
}
}
}
if (sameCount > 5) {
lostPoint += (3 + sameCount - 5);
}
}
};
// LEVEL2
for (var row = 0; row < moduleCount - 1; row += 1) {
for (var col = 0; col < moduleCount - 1; col += 1) {
var count = 0;
if (qrcode.isDark(row, col) ) count += 1;
if (qrcode.isDark(row + 1, col) ) count += 1;
if (qrcode.isDark(row, col + 1) ) count += 1;
if (qrcode.isDark(row + 1, col + 1) ) count += 1;
if (count == 0 || count == 4) {
lostPoint += 3;
}
}
}
// LEVEL3
for (var row = 0; row < moduleCount; row += 1) {
for (var col = 0; col < moduleCount - 6; col += 1) {
if (qrcode.isDark(row, col)
&& !qrcode.isDark(row, col + 1)
&& qrcode.isDark(row, col + 2)
&& qrcode.isDark(row, col + 3)
&& qrcode.isDark(row, col + 4)
&& !qrcode.isDark(row, col + 5)
&& qrcode.isDark(row, col + 6) ) {
lostPoint += 40;
}
}
}
for (var col = 0; col < moduleCount; col += 1) {
for (var row = 0; row < moduleCount - 6; row += 1) {
if (qrcode.isDark(row, col)
&& !qrcode.isDark(row + 1, col)
&& qrcode.isDark(row + 2, col)
&& qrcode.isDark(row + 3, col)
&& qrcode.isDark(row + 4, col)
&& !qrcode.isDark(row + 5, col)
&& qrcode.isDark(row + 6, col) ) {
lostPoint += 40;
}
}
}
// LEVEL4
var darkCount = 0;
for (var col = 0; col < moduleCount; col += 1) {
for (var row = 0; row < moduleCount; row += 1) {
if (qrcode.isDark(row, col) ) {
darkCount += 1;
}
}
}
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
lostPoint += ratio * 10;
return lostPoint;
};
return _this;
}();
//---------------------------------------------------------------------
// QRMath
//---------------------------------------------------------------------
var QRMath = function() {
var EXP_TABLE = new Array(256);
var LOG_TABLE = new Array(256);
// initialize tables
for (var i = 0; i < 8; i += 1) {
EXP_TABLE[i] = 1 << i;
}
for (var i = 8; i < 256; i += 1) {
EXP_TABLE[i] = EXP_TABLE[i - 4]
^ EXP_TABLE[i - 5]
^ EXP_TABLE[i - 6]
^ EXP_TABLE[i - 8];
}
for (var i = 0; i < 255; i += 1) {
LOG_TABLE[EXP_TABLE[i] ] = i;
}
var _this = {};
_this.glog = function(n) {
if (n < 1) {
throw new Error('glog(' + n + ')');
}
return LOG_TABLE[n];
};
_this.gexp = function(n) {
while (n < 0) {
n += 255;
}
while (n >= 256) {
n -= 255;
}
return EXP_TABLE[n];
};
return _this;
}();
//---------------------------------------------------------------------
// qrPolynomial
//---------------------------------------------------------------------
function qrPolynomial(num, shift) {
if (typeof num.length == 'undefined') {
throw new Error(num.length + '/' + shift);
}
var _num = function() {
var offset = 0;
while (offset < num.length && num[offset] == 0) {
offset += 1;
}
var _num = new Array(num.length - offset + shift);
for (var i = 0; i < num.length - offset; i += 1) {
_num[i] = num[i + offset];
}
return _num;
}();
var _this = {};
_this.getAt = function(index) {
return _num[index];
};
_this.getLength = function() {
return _num.length;
};
_this.multiply = function(e) {
var num = new Array(_this.getLength() + e.getLength() - 1);
for (var i = 0; i < _this.getLength(); i += 1) {
for (var j = 0; j < e.getLength(); j += 1) {
num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) );
}
}
return qrPolynomial(num, 0);
};
_this.mod = function(e) {
if (_this.getLength() - e.getLength() < 0) {
return _this;
}
var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) );
var num = new Array(_this.getLength() );
for (var i = 0; i < _this.getLength(); i += 1) {
num[i] = _this.getAt(i);
}
for (var i = 0; i < e.getLength(); i += 1) {
num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio);
}
// recursive call
return qrPolynomial(num, 0).mod(e);
};
return _this;
};
//---------------------------------------------------------------------
// QRRSBlock
//---------------------------------------------------------------------
var QRRSBlock = function() {
// [1: [L, M, Q, H], ..]
var RS_BLOCK_TABLE=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]];
var qrRSBlock = function(totalCount, dataCount) {
var _this = {};
_this.totalCount = totalCount;
_this.dataCount = dataCount;
return _this;
};
var _this = {};
var getRsBlockTable = function(typeNumber, errorCorrectLevel) {
switch(errorCorrectLevel) {
case QRErrorCorrectLevel.L :
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
case QRErrorCorrectLevel.M :
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
case QRErrorCorrectLevel.Q :
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
case QRErrorCorrectLevel.H :
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
default :
return undefined;
}
};
_this.getRSBlocks = function(typeNumber, errorCorrectLevel) {
var rsBlock = getRsBlockTable(typeNumber, errorCorrectLevel);
if (typeof rsBlock == 'undefined') {
throw new Error('bad rs block @ typeNumber:' + typeNumber +
'/errorCorrectLevel:' + errorCorrectLevel);
}
var length = rsBlock.length / 3;
var list = new Array();
for (var i = 0; i < length; i += 1) {
var count = rsBlock[i * 3 + 0];
var totalCount = rsBlock[i * 3 + 1];
var dataCount = rsBlock[i * 3 + 2];
for (var j = 0; j < count; j += 1) {
list.push(qrRSBlock(totalCount, dataCount) );
}
}
return list;
};
return _this;
}();
//---------------------------------------------------------------------
// qrBitBuffer
//---------------------------------------------------------------------
var qrBitBuffer = function() {
var _buffer = new Array();
var _length = 0;
var _this = {};
_this.getBuffer = function() {
return _buffer;
};
_this.getAt = function(index) {
var bufIndex = Math.floor(index / 8);
return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1;
};
_this.put = function(num, length) {
for (var i = 0; i < length; i += 1) {
_this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1);
}
};
_this.getLengthInBits = function() {
return _length;
};
_this.putBit = function(bit) {
var bufIndex = Math.floor(_length / 8);
if (_buffer.length <= bufIndex) {
_buffer.push(0);
}
if (bit) {
_buffer[bufIndex] |= (0x80 >>> (_length % 8) );
}
_length += 1;
};
return _this;
};
//---------------------------------------------------------------------
// qr8BitByte
//---------------------------------------------------------------------
var qr8BitByte = function(data) {
var _mode = QRMode.MODE_8BIT_BYTE;
var _data = data;
var _parsedData = [];
var _this = {};
// Added to support UTF-8 Characters
for (var i = 0, l = _data.length; i < l; i++) {
var byteArray = [];
var code = _data.charCodeAt(i);
if (code > 0x10000) {
byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18);
byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12);
byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[3] = 0x80 | (code & 0x3F);
} else if (code > 0x800) {
byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12);
byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6);
byteArray[2] = 0x80 | (code & 0x3F);
} else if (code > 0x80) {
byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6);
byteArray[1] = 0x80 | (code & 0x3F);
} else {
byteArray[0] = code;
}
// Fix Unicode corruption bug
_parsedData.push(byteArray);
}
_parsedData = Array.prototype.concat.apply([], _parsedData);
if (_parsedData.length != _data.length) {
_parsedData.unshift(191);
_parsedData.unshift(187);
_parsedData.unshift(239);
}
var _bytes = _parsedData;
_this.getMode = function() {
return _mode;
};
_this.getLength = function(buffer) {
return _bytes.length;
};
_this.write = function(buffer) {
for (var i = 0; i < _bytes.length; i += 1) {
buffer.put(_bytes[i], 8);
}
};
return _this;
};
//=====================================================================
// GIF Support etc.
//
//---------------------------------------------------------------------
// byteArrayOutputStream
//---------------------------------------------------------------------
var byteArrayOutputStream = function() {
var _bytes = new Array();
var _this = {};
_this.writeByte = function(b) {
_bytes.push(b & 0xff);
};
_this.writeShort = function(i) {
_this.writeByte(i);
_this.writeByte(i >>> 8);
};
_this.writeBytes = function(b, off, len) {
off = off || 0;
len = len || b.length;
for (var i = 0; i < len; i += 1) {
_this.writeByte(b[i + off]);
}
};
_this.writeString = function(s) {
for (var i = 0; i < s.length; i += 1) {
_this.writeByte(s.charCodeAt(i) );
}
};
_this.toByteArray = function() {
return _bytes;
};
_this.toString = function() {
var s = '';
s += '[';
for (var i = 0; i < _bytes.length; i += 1) {
if (i > 0) {
s += ',';
}
s += _bytes[i];
}
s += ']';
return s;
};
return _this;
};
//---------------------------------------------------------------------
// base64EncodeOutputStream
//---------------------------------------------------------------------
var base64EncodeOutputStream = function() {
var _buffer = 0;
var _buflen = 0;
var _length = 0;
var _base64 = '';
var _this = {};
var writeEncoded = function(b) {
_base64 += String.fromCharCode(encode(b & 0x3f) );
};
var encode = function(n) {
if (n < 0) {
// error.
} else if (n < 26) {
return 0x41 + n;
} else if (n < 52) {
return 0x61 + (n - 26);
} else if (n < 62) {
return 0x30 + (n - 52);
} else if (n == 62) {
return 0x2b;
} else if (n == 63) {
return 0x2f;
}
throw new Error('n:' + n);
};
_this.writeByte = function(n) {
_buffer = (_buffer << 8) | (n & 0xff);
_buflen += 8;
_length += 1;
while (_buflen >= 6) {
writeEncoded(_buffer >>> (_buflen - 6) );
_buflen -= 6;
}
};
_this.flush = function() {
if (_buflen > 0) {
writeEncoded(_buffer << (6 - _buflen) );
_buffer = 0;
_buflen = 0;
}
if (_length % 3 != 0) {
// padding
var padlen = 3 - _length % 3;
for (var i = 0; i < padlen; i += 1) {
_base64 += '=';
}
}
};
_this.toString = function() {
return _base64;
};
return _this;
};
//---------------------------------------------------------------------
// base64DecodeInputStream
//---------------------------------------------------------------------
var base64DecodeInputStream = function(str) {
var _str = str;
var _pos = 0;
var _buffer = 0;
var _buflen = 0;
var _this = {};
_this.read = function() {
while (_buflen < 8) {
if (_pos >= _str.length) {
if (_buflen == 0) {
return -1;
}
throw new Error('unexpected end of file./' + _buflen);
}
var c = _str.charAt(_pos);
_pos += 1;
if (c == '=') {
_buflen = 0;
return -1;
} else if (c.match(/^\s$/) ) {
// ignore if whitespace.
continue;
}
_buffer = (_buffer << 6) | decode(c.charCodeAt(0) );
_buflen += 6;
}
var n = (_buffer >>> (_buflen - 8) ) & 0xff;
_buflen -= 8;
return n;
};
var decode = function(c) {
if (0x41 <= c && c <= 0x5a) {
return c - 0x41;
} else if (0x61 <= c && c <= 0x7a) {
return c - 0x61 + 26;
} else if (0x30 <= c && c <= 0x39) {
return c - 0x30 + 52;
} else if (c == 0x2b) {
return 62;
} else if (c == 0x2f) {
return 63;
} else {
throw new Error('c:' + c);
}
};
return _this;
};
//---------------------------------------------------------------------
// gifImage (B/W)
//---------------------------------------------------------------------
var gifImage = function(width, height) {
var _width = width;
var _height = height;
var _data = new Array(width * height);
var _this = {};
_this.setPixel = function(x, y, pixel) {
_data[y * _width + x] = pixel;
};
_this.write = function(out) {
//---------------------------------
// GIF Signature
out.writeString('GIF87a');
//---------------------------------
// Screen Descriptor
out.writeShort(_width);
out.writeShort(_height);
out.writeByte(0x80); // 2bit
out.writeByte(0);
out.writeByte(0);
//---------------------------------
// Global Color Map
// black
out.writeByte(0x00);
out.writeByte(0x00);
out.writeByte(0x00);
// white
out.writeByte(0xff);
out.writeByte(0xff);
out.writeByte(0xff);
//---------------------------------
// Image Descriptor
out.writeString(',');
out.writeShort(0);
out.writeShort(0);
out.writeShort(_width);
out.writeShort(_height);
out.writeByte(0);
//---------------------------------
// Local Color Map
//---------------------------------
// Raster Data
var lzwMinCodeSize = 2;
var raster = getLZWRaster(lzwMinCodeSize);
out.writeByte(lzwMinCodeSize);
var offset = 0;
while (raster.length - offset > 255) {
out.writeByte(255);
out.writeBytes(raster, offset, 255);
offset += 255;
}
out.writeByte(raster.length - offset);
out.writeBytes(raster, offset, raster.length - offset);
out.writeByte(0x00);
//---------------------------------
// GIF Terminator
out.writeString(';');
};
var bitOutputStream = function(out) {
var _out = out;
var _bitLength = 0;
var _bitBuffer = 0;
var _this = {};
_this.write = function(data, length) {
if ( (data >>> length) != 0) {
throw new Error('length over');
}
while (_bitLength + length >= 8) {
_out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) );
length -= (8 - _bitLength);
data >>>= (8 - _bitLength);
_bitBuffer = 0;
_bitLength = 0;
}
_bitBuffer = (data << _bitLength) | _bitBuffer;
_bitLength = _bitLength + length;
};
_this.flush = function() {
if (_bitLength > 0) {
_out.writeByte(_bitBuffer);
}
};
return _this;
};
var getLZWRaster = function(lzwMinCodeSize) {
var clearCode = 1 << lzwMinCodeSize;
var endCode = (1 << lzwMinCodeSize) + 1;
var bitLength = lzwMinCodeSize + 1;
// Setup LZWTable
var table = lzwTable();
for (var i = 0; i < clearCode; i += 1) {
table.add(String.fromCharCode(i) );
}
table.add(String.fromCharCode(clearCode) );
table.add(String.fromCharCode(endCode) );
var byteOut = byteArrayOutputStream();
var bitOut = bitOutputStream(byteOut);
// clear code
bitOut.write(clearCode, bitLength);
var dataIndex = 0;
var s = String.fromCharCode(_data[dataIndex]);
dataIndex += 1;
while (dataIndex < _data.length) {
var c = String.fromCharCode(_data[dataIndex]);
dataIndex += 1;
if (table.contains(s + c) ) {
s = s + c;
} else {
bitOut.write(table.indexOf(s), bitLength);
if (table.size() < 0xfff) {
if (table.size() == (1 << bitLength) ) {
bitLength += 1;
}
table.add(s + c);
}
s = c;
}
}
bitOut.write(table.indexOf(s), bitLength);
// end code
bitOut.write(endCode, bitLength);
bitOut.flush();
return byteOut.toByteArray();
};
var lzwTable = function() {
var _map = {};
var _size = 0;
var _this = {};
_this.add = function(key) {
if (_this.contains(key) ) {
throw new Error('dup key:' + key);
}
_map[key] = _size;
_size += 1;
};
_this.size = function() {
return _size;
};
_this.indexOf = function(key) {
return _map[key];
};
_this.contains = function(key) {
return typeof _map[key] != 'undefined';
};
return _this;
};
return _this;
};
var createImgTag = function(width, height, getPixel, alt) {
var gif = gifImage(width, height);
for (var y = 0; y < height; y += 1) {
for (var x = 0; x < width; x += 1) {
gif.setPixel(x, y, getPixel(x, y) );
}
}
var b = byteArrayOutputStream();
gif.write(b);
var base64 = base64EncodeOutputStream();
var bytes = b.toByteArray();
for (var i = 0; i < bytes.length; i += 1) {
base64.writeByte(bytes[i]);
}
base64.flush();
var img = '';
img += 'data:image/gif;base64,';
img += base64;
return img;
};
//---------------------------------------------------------------------
// returns qrcode function.
var drawImg = function(text, options) {
options = options || {};
var typeNumber = options.typeNumber || 4;
var errorCorrectLevel = options.errorCorrectLevel || 'M';
var size = options.size || 500;
var qr;
try {
qr = qrcode(typeNumber, errorCorrectLevel || 'M');
qr.addData(text);
qr.make();
} catch (e) {
if(typeNumber >= 40) {
throw new Error('Text too long to encode');
} else {
return drawImg(text, {
size: size,
errorCorrectLevel: errorCorrectLevel,
typeNumber: typeNumber + 1
});
}
}
// calc cellsize and margin
var cellsize = parseInt(size / qr.getModuleCount());
var margin = parseInt((size - qr.getModuleCount() * cellsize) / 2);
return qr.createImgTag(cellsize, margin, size);
};
module.exports = {
drawImg: drawImg
};
================================================
FILE: miniprogram/pages/test1/test1.js
================================================
// pages/test1/test1.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
================================================
FILE: miniprogram/pages/test1/test1.wxml
================================================
pages/test1/test1.wxml
================================================
FILE: miniprogram/projects/TRIP1/biz/admin_album_biz.js
================================================
/**
* Notes: 相册后台管理模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-05 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const AlbumBiz = require('./album_biz.js');
const projectSetting = require('../public/project_setting.js');
class AdminAlbumBiz extends BaseBiz {
static initFormData(id = '') {
let cateIdOptions = AlbumBiz.getCateList();
return {
id,
cateIdOptions,
fields: projectSetting.ALBUM_FIELDS,
formTitle: '',
formCateId: (cateIdOptions.length == 1) ? cateIdOptions[0].val : '',
formOrder: 9999,
formForms: [],
}
}
}
AdminAlbumBiz.CHECK_FORM = {
title: 'formTitle|must|string|min:2|max:50|name=标题',
cateId: 'formCateId|must|id|name=分类',
order: 'formOrder|must|int|min:0|max:9999|name=排序号',
forms: 'formForms|array',
};
module.exports = AdminAlbumBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/admin_meet_biz.js
================================================
/**
* Notes:预约后台管理模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const cloudHelper = require('../../../helper/cloud_helper.js');
const dataHelper = require('../../../helper/data_helper.js');
const timeHelper = require('../../../helper/time_helper.js');
const projectSetting = require('../public/project_setting.js');
const formSetHelper = require('../../../cmpts/public/form/form_set_helper.js');
const TIME_NODE = {
mark: 'mark-no',
start: '09:00', //开始
end: '21:30', // 结束
limit: 50, //人数限制
isLimit: false,
status: 1,
stat: { //统计数据
succCnt: 0,
cancelCnt: 0,
adminCancelCnt: 0,
}
};
class AdminMeetBiz extends BaseBiz {
/** 取得分类 */
static getTypeList() {
let typeList = projectSetting.MEET_TYPE;
let arr = [];
for (let k = 0; k < typeList.length; k++) {
arr.push({
label: typeList[k].title,
type: 'typeId',
val: typeList[k].id, //for options
value: typeList[k].id, //for list
})
}
return arr;
}
static getTypeName(typeId) {
let typeList = projectSetting.MEET_TYPE;
for (let k = 0; k < typeList.length; k++) {
if (typeList[k].id == typeId) return typeList[k].title;
}
return '';
}
// 计算剩余天数
static getLeaveDay(days) {
let now = timeHelper.time('Y-M-D');
let count = 0;
for (let k = 0; k < days.length; k++) {
if (days[k].day >= now) count++;
}
return count;
}
static getNewTimeNode(day) {
let node = dataHelper.deepClone(TIME_NODE);
day = day.replace(/-/g, '');
node.mark = 'T' + day + 'AAA' + dataHelper.genRandomAlpha(10).toUpperCase();
return node;
}
static getDaysTimeOptions() {
let HourArr = [];
let clockArr = [];
let k = 0;
for (k = 0; k <= 23; k++) {
let node = {};
node.label = k + '点';
node.val = k < 10 ? '0' + k : k;
HourArr.push(node);
}
for (k = 0; k < 59;) {
let node = {};
node.label = k + '分';
node.val = k < 10 ? '0' + k : k;
clockArr.push(node);
k += 5;
if (k == 60) {
node = {};
node.label = '59分';
node.val = '59';
clockArr.push(node);
}
}
return [HourArr, clockArr];
}
static getEndBeforeSetOptions() {
let ret = '';
let k = 0;
for (k = 0; k < 60;) {
ret += ',' + k + '=预约时段开始前' + k + '分钟截止';
k += 10;
}
for (k = 60; k < 360;) {
ret += ',' + k + '=预约时段开始前' + k / 60 + '小时截止';
k += 30;
}
for (k = 360; k <= 48 * 60;) {
ret += ',' + k + '=预约时段开始前' + k / 60 + '小时截止';
k += 60;
}
if (ret.startsWith(',')) ret = ret.substring(1);
return dataHelper.getSelectOptions(ret);
}
static getEndYesterdaySetOptions() {
let ret = '';
let k = 0;
for (k = 0; k < 24; k++) {
ret += ',' + (k < 10 ? '0' + k : k) + ':00=前一天' + k + '点00分' + '截止';
ret += ',' + (k < 10 ? '0' + k : k) + ':30=前一天' + k + '点30分' + '截止';
}
ret += ',23:59=前一天23点59分截止';
if (ret.startsWith(',')) ret = ret.substring(1);
return dataHelper.getSelectOptions(ret);
}
static getEndTodaySetOptions() {
let ret = '';
let k = 0;
for (k = 0; k < 24; k++) {
ret += ',' + (k < 10 ? '0' + k : k) + ':00=当天' + k + '点00分' + '截止';
ret += ',' + (k < 10 ? '0' + k : k) + ':30=当天' + k + '点30分' + '截止';
}
ret += ',23:59=当天23点59分截止';
if (ret.startsWith(',')) ret = ret.substring(1);
return dataHelper.getSelectOptions(ret);
}
static getEndAfterSetOptions() {
let ret = '';
let k = 0;
for (k = 0; k < 60;) {
ret += ',' + k + '=预约时段开始后' + k + '分钟截止';
k += 5;
}
for (k = 60; k <= 3 * 60;) {
ret += ',' + k + '=预约时段开始后' + k / 60 + '小时截止';
k += 30;
}
if (ret.startsWith(',')) ret = ret.substring(1);
return dataHelper.getSelectOptions(ret);
}
static getBeginDaySetOptions() {
let dayArr = [];
let clockArr = [];
let k = 0;
let nodeCur = {};
nodeCur.label = '当天';
nodeCur.val = 0;
dayArr.push(nodeCur);
for (k = 1; k <= 180; k++) {
let node = {};
node.label = '提前' + k + '天';
node.val = k;
dayArr.push(node);
}
for (k = 0; k < 24; k++) {
let node = {};
node.label = k + '点00分' + '开始';
node.val = (k < 10 ? '0' + k : k) + ':00';
clockArr.push(node);
node = {};
node.label = k + '点30分' + '开始';
node.val = (k < 10 ? '0' + k : k) + ':30';
clockArr.push(node);
}
return [dayArr, clockArr];
}
static getCancelSetOptions() {
let modeArr = [{
label: '取消后无须后台审核',
val: 'no'
}
/*, {
label: '取消后须后台审核',
val: 'check'
}*/
];
let timeArr = [];
let k = 0;
for (k = -60; k < 0;) {
let node = {};
node.label = '开始后' + (-k) + '分钟内可取消';
node.val = k;
timeArr.push(node);
k += 10;
}
for (k = 0; k < 60;) {
let node = {};
node.label = '开始前' + k + '分钟可取消';
node.val = k;
timeArr.push(node);
k += 10;
}
for (k = 60; k < 60 * 24;) {
let node = {};
node.label = '开始前' + k / 60 + '小时可取消';
node.val = k;
timeArr.push(node);
k += 60;
}
for (k = 60 * 24; k <= 60 * 24 * 10;) {
let node = {};
node.label = '开始前' + k / (60 * 24) + '天可取消';
node.val = k;
timeArr.push(node);
k += 60 * 24;
}
return [timeArr, modeArr];
}
static getLimitSetOptions() {
let mode = dataHelper.getSelectOptions('all=本项目全程限制次数,clock=按每一时段限制次数,day=按每天限制次数,week=按自然周限制次数,month=按自然月限制次数');
let list = [];
for (let k = 0; k < mode.length; k++) {
let node = {};
node.label = mode[k].label;
node.val = mode[k].val;
let children = [];
if (k == 0) {
children.push({
label: '不限制预约次数',
val: -1
});
}
for (let j = 1; j <= 30; j++) {
let childNode = {};
childNode.label = '可预约' + j + '次';
childNode.val = j
children.push(childNode);
}
node.children = children;
list.push(node);
}
return list;
}
// 上限规则的表述
static getLimitSetDesc(rule) {
let ret = '';
switch (rule.mode) {
case 'all':
ret = rule.cnt > 0 ? '本项目全程可预约' + rule.cnt + '次' : '本项目全程不限制次数';
break;
case 'month':
ret = '自然月内可预约' + rule.cnt + '次';
break;
case 'week':
ret = '自然周内可预约' + rule.cnt + '次';
break;
case 'day':
ret = '每天可预约' + rule.cnt + '次';
break;
case 'clock':
ret = '每一时段可预约' + rule.cnt + '次';
break;
}
return ret;
}
// 截止规则的表述
static getEndSetDesc(rule) {
let ret = '';
switch (rule.mode) {
case 'no':
ret = '不限制';
break;
case 'yesterday':
ret = '前一天' + rule.time + '预约截止';
break;
case 'today':
ret = '当天' + rule.time + '预约截止';
break;
case 'clock':
ret = rule.time + '预约截止';
break;
case 'before':
if (rule.time < 60)
ret = '开始前' + rule.time + '分钟预约截止';
else
ret = '开始前' + rule.time / 60 + '小时预约截止';
break;
case 'after':
if (rule.time < 60)
ret = '开始后' + rule.time + '分钟预约截止';
else
ret = '开始后' + rule.time / 60 + '小时预约截止';
break;
}
return ret;
}
// 取消规则的表述
static getCancelSetDesc(rule) {
let ret = '';
switch (rule.mode) {
case 'no':
if (rule.time < 0)
ret = '开始后' + (-rule.time) + '分钟可取消,无须审核';
else if (rule.time == 0)
ret = '开始前均可取消,无须审核';
else if (rule.time < 60)
ret = '开始前' + rule.time + '分钟可取消,无须审核';
else if (rule.time < 1440)
ret = '开始前' + rule.time / 60 + '小时可取消,无须审核';
else
ret = '开始前' + rule.time / (60 * 24) + '天可取消,无须审核';
break;
case 'check':
if (rule.time == 60)
ret = '开始前均可取消,须审核';
else if (rule.time < 60)
ret = '开始前' + rule.time + '分钟可取消,须审核';
else if (rule.time < 1440)
ret = '开始前' + rule.time / 60 + '小时可取消,无须审核';
else
ret = '开始前' + rule.time / (60 * 24) + '天可取消,须审核';
break;
}
ret = ret.replace(',无须审核', '');
return ret;
}
// 开放规则的表述
static getBeginSetDesc(rule) {
let ret = '';
switch (rule.mode) {
case 'no':
ret = '随时可预约';
break;
case 'day':
if (rule.day == 0)
ret = '当天 ' + rule.time + '开放预约';
else
ret = '提前' + rule.day + '天 ' + rule.time + '开放预约';
break;
case 'clock':
ret = rule.time + '起开放预约';
break;
}
return ret;
}
/** 表单初始化相关数据 */
static async initFormData() {
let typeIdOptions = AdminMeetBiz.getTypeList();
return {
// 选项数据
typeIdOptions,
beginDaySetOptions: AdminMeetBiz.getBeginDaySetOptions(),
// 表单数据
formTitle: '',
formTypeId: (typeIdOptions.length == 1) ? typeIdOptions[0].val : '',
formContent: '',
formOrder: 9999,
formStyleSet: {
pic: '',
desc: ''
},
formDaysSet: [], // 时间设置
formIsShowLimit: 1, //是否显示可预约数量
formFormSet: formSetHelper.initFields(projectSetting.MEET_JOIN_FIELDS)
}
}
/**
* 样式更新
* @param {string} meetId
* @param {Array} content 富文本数组
*/
static async updateMeetStyleSet(that, meetId, styleSet) {
let pic = styleSet.pic;
// 图片上传到云空间
if (styleSet.pic)
pic = await cloudHelper.transTempPicOne(pic, 'meet/', meetId, false);
styleSet.pic = pic;
// 更新本记录的图片信息
let params = {
meetId,
styleSet
}
try {
// 更新数据 从promise 里直接同步返回
await cloudHelper.callCloudSumbit('admin/meet_update_style', params);
that.setData({
formStyleSet: styleSet
});
} catch (e) {
console.error(e);
return false;
}
return true;
}
/**
* 富文本中的图片上传
* @param {string} meetId
* @param {Array} content 富文本数组
*/
static async updateMeetCotnentPic(that, meetId, content) {
let imgList = [];
for (let k = 0; k < content.length; k++) {
if (content[k].type == 'img') {
imgList.push(content[k].val);
}
}
// 图片上传到云空间
imgList = await cloudHelper.transTempPics(imgList, 'meet/', meetId);
// 更新图片地址
let imgIdx = 0;
for (let k = 0; k < content.length; k++) {
if (content[k].type == 'img') {
content[k].val = imgList[imgIdx];
imgIdx++;
}
}
// 更新本记录的图片信息
let params = {
meetId,
content
}
try {
// 更新数据 从promise 里直接同步返回
await cloudHelper.callCloudSumbit('admin/meet_update_content', params);
that.setData({
formContent: content
});
} catch (e) {
console.error(e);
return false;
}
return true;
}
}
/** 表单校验 */
AdminMeetBiz.CHECK_FORM = {
title: 'formTitle|must|string|min:2|max:50|name=标题',
typeId: 'formTypeId|must|id|name=分类',
order: 'formOrder|must|int|min:0|max:9999|name=排序号',
daysSet: 'formDaysSet|must|array|name=预约时间设置',
isShowLimit: 'formIsShowLimit|must|int|in:0,1|name=是否显示可预约人数',
formSet: 'formFormSet|must|array|name=用户资料设置',
};
module.exports = AdminMeetBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/admin_news_biz.js
================================================
/**
* Notes: 资讯后台管理模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const NewsBiz = require('./news_biz.js');
const projectSetting = require('../public/project_setting.js');
class AdminNewsBiz extends BaseBiz {
/** 表单初始化相关数据 */
static initFormData(id = '') {
let cateIdOptions = NewsBiz.getCateList();
return {
id,
contentDesc: '',
// 分类
cateIdOptions,
fields: projectSetting.NEWS_FIELDS,
// 图片数据
imgList: [],
// 表单数据
formOrder: 9999,
formTitle: '',
formDesc: '',
formContent: [],
formCateId: (cateIdOptions.length == 1) ? cateIdOptions[0].val : '',
formForms:[],
}
}
static getCateName(cateId) {
let cateList = projectSetting.NEWS_CATE;
for (let k = 0; k < cateList.length; k++) {
if (cateList[k].id == cateId) return cateList[k].title;
}
return '';
}
}
/** 表单校验 本地 */
AdminNewsBiz.CHECK_FORM = {
title: 'formTitle|must|string|min:4|max:50|name=标题',
cateId: 'formCateId|must|id|name=分类',
order: 'formOrder|must|int|min:0|max:9999|name=排序号',
desc: 'formDesc|string|min:10|max:200|name=简介',
forms: 'formForms|array',
};
module.exports = AdminNewsBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/admin_product_biz.js
================================================
/**
* Notes: 相册后台管理模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-06-05 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const ProductBiz = require('./product_biz.js');
const projectSetting = require('../public/project_setting.js');
class AdminProductBiz extends BaseBiz {
static initFormData(id = '') {
let cateIdOptions = ProductBiz.getCateList();
return {
id,
cateIdOptions,
fields: projectSetting.PRODUCT_FIELDS,
formTitle: '',
formCateId: (cateIdOptions.length == 1) ? cateIdOptions[0].val : '',
formOrder: 9999,
formForms: [],
}
}
}
AdminProductBiz.CHECK_FORM = {
title: 'formTitle|must|string|min:2|max:50|name=标题',
cateId: 'formCateId|must|id|name=分类',
order: 'formOrder|must|int|min:0|max:9999|name=排序号',
forms: 'formForms|array',
};
module.exports = AdminProductBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/album_biz.js
================================================
/**
* Notes: 相册模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const projectSetting = require('../public/project_setting.js');
class AlbumBiz extends BaseBiz {
static getCateName(cateId) {
return BaseBiz.getCateName(cateId, projectSetting.ALBUM_CATE);
}
static getCateList() {
return BaseBiz.getCateList(projectSetting.ALBUM_CATE);
}
static setCateTitle() {
return BaseBiz.setCateTitle(projectSetting.ALBUM_CATE);
}
/** 搜索菜单设置 */
static async getSearchMenu() {
let sortMenus = [{
label: '全部',
type: '',
value: ''
}];
let sortMenusAfter = [{
label: '最新',
type: 'sort',
value: 'new'
},];
let sortItems = [];
sortMenus = sortMenus.concat(sortMenusAfter);
return {
sortItems,
sortMenus
}
}
}
module.exports = AlbumBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/meet_biz.js
================================================
/**
* Notes: 预约模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2021-12-10 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const pageHelper = require('../../../helper/page_helper.js');
const dataHelper = require('../../../helper/data_helper.js');
const projectSetting = require('../public/project_setting.js');
class MeetBiz extends BaseBiz {
static async subscribeMessageMeet(callback) {
callback && await callback();
}
static setTypeTitle(that, typeId = null) {
let typeList = projectSetting.MEET_TYPE ;
for (let k = 0; k < typeList.length; k++) {
if (typeList[k].id == typeId) {
wx.setNavigationBarTitle({
title: typeList[k].title
});
if (typeList[k].style) { //样式
that.setData({
listMode: typeList[k].style
});
} else {
that.setData({
listMode: 'leftpic'
});
}
}
}
return '';
}
}
module.exports = MeetBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/news_biz.js
================================================
/**
* Notes: 资讯模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const dataHelper = require('../../../helper/data_helper.js');
const projectSetting = require('../public/project_setting.js');
class NewsBiz extends BaseBiz {
/** 取得分类 */
static getCateList() {
let cateList = projectSetting.NEWS_CATE;
let arr = [];
for (let k = 0; k < cateList.length; k++) {
arr.push({
label: cateList[k].title,
type: 'cateId',
val: cateList[k].id, //for options
value: cateList[k].id, //for list
})
}
return arr;
}
static setCateTitle(cateId = null, that) {
// 获取当前小程序的页面栈
let pages = getCurrentPages();
// 数组中索引最大的页面--当前页面
let currentPage = pages[pages.length - 1];
// 附加参数
if (currentPage.options && currentPage.options.id) {
cateId = currentPage.options.id;
}
let cateList = dataHelper.getSelectOptions(projectSetting.NEWS_CATE);
for (let k = 0; k < cateList.length; k++) {
if (cateList[k].val == cateId) {
wx.setNavigationBarTitle({
title: cateList[k].label
});
if (cateList[k].ext) { //样式
that.setData({
listMode: cateList[k].ext
});
} else {
that.setData({
listMode: 'leftbig'
});
}
}
}
}
/** 搜索菜单设置 */
static async getSearchMenu() {
let sortMenus = [{
label: '全部',
type: '',
value: ''
}];
let sortMenusAfter = [{
label: '最新',
type: 'sort',
value: 'new'
},];
let sortItems = [];
sortMenus = sortMenus.concat(sortMenusAfter);
return {
sortItems,
sortMenus
}
}
}
module.exports = NewsBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/product_biz.js
================================================
/**
* Notes: 产品模块业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-11-14 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const projectSetting = require('../public/project_setting.js');
class ProductBiz extends BaseBiz {
static getCateName(cateId) {
return BaseBiz.getCateName(cateId, projectSetting.PRODUCT_CATE);
}
static getCateList() {
return BaseBiz.getCateList(projectSetting.PRODUCT_CATE);
}
static setCateTitle() {
return BaseBiz.setCateTitle(projectSetting.PRODUCT_CATE);
}
/** 搜索菜单设置 */
static async getSearchMenu() {
let sortMenus = [{
label: '全部',
type: '',
value: ''
}];
let sortMenusAfter = [{
label: '最新',
type: 'sort',
value: 'new'
},];
let sortItems = [];
sortMenus = sortMenus.concat(sortMenusAfter);
return {
sortItems,
sortMenus
}
}
}
module.exports = ProductBiz;
================================================
FILE: miniprogram/projects/TRIP1/biz/project_biz.js
================================================
/**
* Notes: 项目通用业务逻辑
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2022-05-22 07:48:00
*/
const BaseBiz = require('../../../comm/biz/base_biz.js');
const projectSetting = require('../public/project_setting.js');
const PubilcBiz = require('../../../comm/biz/public_biz.js');
const PassportBiz = require('../../../comm/biz/passport_biz.js');
class ProjectBiz extends BaseBiz {
/**
* 页面初始化
* @param {*} that
*/
static initPage(that, { isSetNavColor = true } = {}) {
let skin = {};
skin.NAV_BG = projectSetting.NAV_BG;
skin.NAV_COLOR = projectSetting.NAV_COLOR;
skin.PROJECT_COLOR = projectSetting.PROJECT_COLOR;
PubilcBiz.initPageBase(that, { skin, isSetNavColor });
}
}
module.exports = ProjectBiz;
================================================
FILE: miniprogram/projects/TRIP1/pages/about/index/about_index.js
================================================
const behavior = require('../../../../../comm/behavior/about_bh.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const projectSetting = require('../../../public/project_setting.js');
Page({
behaviors: [behavior],
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (options && options.key)
this._loadDetail(options.key, projectSetting.SETUP_CONTENT_ITEMS);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/about/index/about_index.json
================================================
{
"usingComponents": {
},
"enablePullDownRefresh": true
}
================================================
FILE: miniprogram/projects/TRIP1/pages/about/index/about_index.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/about/index/about_index.wxss
================================================
@import "../../../../../style/public/detail.wxss";
@import "../../../style/skin.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/about/service/about_service.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
ProjectBiz.initPage(this);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
},
url:function(e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/about/service/about_service.json
================================================
{
"usingComponents": {},
"navigationBarTitleText": "服务"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/about/service/about_service.wxml
================================================
游玩保障服务
旅游咨询
12301
投诉求助
投诉有门 实时高效
旅游公共服务
公交信息
智慧公交 智慧旅游
停车场
合理分配 高质高效
找厕所
便民信息 方便你我
星级旅行社
星级服务 星级品质
星级导游
服务标兵 宾至如归
一周天气
一手天气 尽在掌握
================================================
FILE: miniprogram/projects/TRIP1/pages/about/service/about_service.wxss
================================================
@import "../../../style/skin.wxss";
page {
background-color: #fff;
}
.main-service {
background-color: #fff;
width: 100%;
display: flex;
flex-direction: column;
padding: 10rpx 0rpx 100rpx;
}
.main-service .pic {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 20rpx;
}
.main-service .pic image {
width: 100%;
height: 260rpx;
border-radius: 20rpx;
}
.service-title {
font-size: 36rpx;
padding: 20rpx 30rpx;
font-weight: bold;
color: #000;
}
.service-list {
width: 100%;
display: flex;
flex-wrap: wrap;
padding: 0 10rpx;
}
.service-list .item {
width: 50%;
padding: 15rpx 15rpx;
}
.service-list .item .item-inner {
width: 100%;
background-color: #06B2DE;
color: #fff;
height: 130rpx;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
.service-list .item .bg1 {
background-image: linear-gradient(45deg, #06B2DE, #01DBC6);
}
.service-list .item .item-inner .bg2 {
background-image: linear-gradient(45deg, #276eb0, #2B97FF);
}
.service-list .item .bg3 {
background-image: linear-gradient(45deg, #10CB61, #7EE263);
}
.service-list .item .bg4 {
background-image: linear-gradient(45deg, #FE753B, #F8BF1B);
}
.service-list .item .bg5 {
background-image: linear-gradient(45deg, #8467FE, #DB81F1);
}
.service-list .item .bg6 {
background-image: linear-gradient(45deg, #FD5589, #FC8563);
}
.service-list .item .left {
flex: 1;
display: flex;
flex-direction: column;
width: 100%;
padding: 0 10rpx 0 20rpx;
}
.service-list .item .item-inner .left .line1 {
font-size: 32rpx;
}
.service-list .item .item-inner .left .line2 {
font-size: 24rpx;
}
.service-list .item .item-inner .right {
font-size: 50rpx;
width: 80rpx;
height: 80rpx;
background-color: rgba(255, 255, 255, .2);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
}
.service-list .item .item-inner .right image {
width: 50rpx;
height: 50rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/add/admin_album_add.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const PublicBiz = require('../../../../../../comm/biz/public_biz.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
const AdminAlbumBiz = require('../../../../biz/admin_album_biz.js');
const AlbumBiz = require('../../../../biz/album_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
this.setData(AdminAlbumBiz.initFormData());
this.setData({
isLoad: true
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
url: function (e) {
pageHelper.url(e, this);
},
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
let data = this.data;
data = validate.check(data, AdminAlbumBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
data.cateName = AlbumBiz.getCateName(data.cateId);
try {
// 创建
let result = await cloudHelper.callCloudSumbit('admin/album_insert', data);
let albumId = result.data.id;
// 图片
await cloudHelper.transFormsTempPics(forms, 'album/', albumId, 'admin/album_update_forms');
let callback = async function () {
PublicBiz.removeCacheList('admin-album-list');
PublicBiz.removeCacheList('album-list');
wx.navigateBack();
}
pageHelper.showSuccToast('添加成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/add/admin_album_add.json
================================================
{
"usingComponents": {
"cmpt-img-upload": "/cmpts/public/img/img_upload_cmpt",
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "后台-攻略添加"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/add/admin_album_add.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/add/admin_album_add.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/admin_album_form_tpl.wxml
================================================
标题
{{formTitleFocus}}
分类
{{formCateIdFocus}}
排序号(小的先显示)
{{formOrderFocus}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/edit/admin_album_edit.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
const AdminAlbumBiz = require('../../../../biz/admin_album_biz.js');
const AlbumBiz = require('../../../../biz/album_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
this.selectComponent("#cmpt-form").reload();
wx.stopPullDownRefresh();
},
model: function (e) {
pageHelper.model(this, e);
},
_loadDetail: async function () {
if (!AdminBiz.isAdmin(this)) return;
let id = this.data.id;
if (!id) return;
if (!this.data.isLoad) this.setData(AdminAlbumBiz.initFormData(id)); // 初始化表单数据
let params = {
id
};
let opt = {
title: 'bar'
};
let album = await cloudHelper.callCloudData('admin/album_detail', params, opt);
if (!album) {
this.setData({
isLoad: null
})
return;
};
this.setData({
isLoad: true,
formTitle: album.ALBUM_TITLE,
formCateId: album.ALBUM_CATE_ID,
formOrder: album.ALBUM_ORDER,
formForms: album.ALBUM_FORMS,
});
},
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
// 数据校验
let data = this.data;
data = validate.check(data, AdminAlbumBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
data.cateName = AlbumBiz.getCateName(data.cateId);
try {
let albumId = this.data.id;
data.id = albumId;
// 先修改,再上传
await cloudHelper.callCloudSumbit('admin/album_edit', data);
await cloudHelper.transFormsTempPics(forms, 'album/', albumId, 'admin/album_update_forms');
let callback = async () => {
// 更新列表页面数据
let node = {
'ALBUM_TITLE': data.title,
'ALBUM_CATE_NAME': data.cateName,
'ALBUM_ORDER': data.order,
}
pageHelper.modifyPrevPageListNodeObject(albumId, node);
wx.navigateBack();
}
pageHelper.showSuccToast('修改成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
url: function (e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/edit/admin_album_edit.json
================================================
{
"usingComponents": {
"cmpt-img-upload": "/cmpts/public/img/img_upload_cmpt",
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "后台-攻略修改"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/edit/admin_album_edit.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/edit/admin_album_edit.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/list/admin_album_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const AlbumBiz = require('../../../../biz/album_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
//设置搜索菜单
this._getSearchMenu();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
bindStatusMoreTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let itemList = ['启用', '停用 (不显示)', '删除'];
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //启用
e.currentTarget.dataset['status'] = 1;
await this._setStatus(e);
break;
}
case 1: { //停止
e.currentTarget.dataset['status'] = 0;
await this._setStatus(e);
break;
}
case 2: { //删除
await this._del(e);
break;
}
}
},
fail: function (res) { }
})
},
bindMoreTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let idx = pageHelper.dataset(e, 'idx');
let order = this.data.dataList.list[idx].ALBUM_ORDER;
let orderDesc = (order == 0) ? '取消置顶' : '置顶';
let vouch = this.data.dataList.list[idx].ALBUM_VOUCH;
let vouchDesc = (vouch == 0) ? '推荐到首页' : '取消首页推荐';
let itemList = ['预览', orderDesc, vouchDesc, '生成专属二维码'];
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //预览
let id = pageHelper.dataset(e, 'id');
wx.navigateTo({
url: '../../../album/detail/album_detail?id=' + id,
});
break;
}
case 1: { //置顶
let sort = (order == 0) ? 9999 : 0;
e.currentTarget.dataset['sort'] = sort;
await this._setSort(e);
break;
}
case 2: { //上首页
vouch = (vouch == 0) ? 1 : 0;
e.currentTarget.dataset['vouch'] = vouch;
await this._setVouch(e);
break;
}
case 3: { //二维码
let title = encodeURIComponent(pageHelper.dataset(e, 'title'));
let qr = encodeURIComponent(pageHelper.dataset(e, 'qr'));
wx.navigateTo({
url: `../../setup/qr/admin_setup_qr?title=${title}&qr=${qr}`,
})
break;
}
}
},
fail: function (res) { }
})
},
_setSort: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let sort = pageHelper.dataset(e, 'sort');
if (!id) return;
let params = {
id,
sort
}
try {
await cloudHelper.callCloudSumbit('admin/album_sort', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'ALBUM_ORDER', sort);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_setVouch: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let vouch = pageHelper.dataset(e, 'vouch');
if (!id) return;
let params = {
id,
vouch
}
try {
await cloudHelper.callCloudSumbit('admin/album_vouch', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'ALBUM_VOUCH', vouch);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_del: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let params = {
id
}
let callback = async () => {
try {
let opts = {
title: '删除中'
}
await cloudHelper.callCloudSumbit('admin/album_del', params, opts).then(res => {
pageHelper.delListNode(id, this.data.dataList.list, '_id');
this.data.dataList.total--;
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('删除成功');
});
} catch (err) {
console.log(err);
}
}
pageHelper.showConfirm('确认删除?删除不可恢复', callback);
},
_setStatus: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let status = Number(pageHelper.dataset(e, 'status'));
let params = {
id,
status
}
try {
await cloudHelper.callCloudSumbit('admin/album_status', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'ALBUM_STATUS', status, '_id');
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_getSearchMenu: function () {
let cateIdOptions = AlbumBiz.getCateList();
let sortItem1 = [{ label: '分类', type: '', value: 0 }];
sortItem1 = sortItem1.concat(cateIdOptions);
let sortItem2 = [
{ label: '排序', type: '', value: 0 }
];
let sortItems = [];
if (sortItem1.length > 2) sortItems.push(sortItem1);
let sortMenus = [
{ label: '全部', type: '', value: '' },
{ label: '正常', type: 'status', value: 1 },
{ label: '停用', type: 'status', value: 0 },
{ label: '最新', type: 'sort', value: 'new' },
{ label: '首页推荐', type: 'vouch', value: 'vouch' },
{ label: '置顶', type: 'top', value: 'top' },
]
this.setData({
cateIdOptions,
sortItems,
sortMenus
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/list/admin_album_list.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"disableScroll": true,
"navigationBarTitleText": "后台-攻略"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/list/admin_album_list.wxml
================================================
共有{{dataList.total}}条符合条件记录
{{index+1}}
分类
:
『{{item.ALBUM_CATE_NAME}}』
排序号
:
{{item.ALBUM_ORDER}} (小的先显示)
创建
:
{{item.ALBUM_ADD_TIME}}
编辑
状态管理..
更多...
预览
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/album/list/admin_album_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
page {
background-color: #f8f8f8;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/content/admin_content.js
================================================
const AdminBiz = require('../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../helper/page_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
formContent: [{
type: 'text',
val: '',
}]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
let formContent = parent.data.formContent;
if (formContent && formContent.length > 0)
this.setData({
formContent
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
},
model: function (e) {
pageHelper.model(this, e);
},
url: function (e) {
pageHelper.url(e, this);
},
bindSaveTap: function (e) {
let formContent = this.selectComponent("#contentEditor").getNodeList();
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
parent.setData({
formContent
}, () => {
parent._setContentDesc();
});
wx.navigateBack();
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/content/admin_content.json
================================================
{
"usingComponents": {
"cmpt-editor": "/cmpts/public/editor/editor_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "后台-详细内容编辑"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/content/admin_content.wxml
================================================
不保存,返回
保存
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/content/admin_content.wxss
================================================
@import '../../../../../style/public/admin.wxss';
.main-admin {
width: 100%;
box-sizing: border-box;
padding: 30rpx 20rpx;
padding-bottom: 200rpx;
}
.form-group {
padding: 1rpx 1rpx;
overflow: hidden;
}
.oprt {
display: flex;
width: 100%;
justify-content: space-around;
}
.oprt button {
width: 45%;
}
.bottom-oprt {
position: fixed;
bottom: 0;
height: 130rpx;
background-color: #f8f8f8;
display: flex;
justify-content: space-around;
align-items: center;
border-top: 1rpx solid #ccc;
z-index: 99999;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/home/admin_home.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
this._loadDetail();
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
_loadDetail: async function () {
let admin = AdminBiz.getAdminToken();
this.setData({
isLoad: true,
admin
});
try {
let opts = {
title: 'bar'
}
let res = await cloudHelper.callCloudData('admin/home', {}, opts);
this.setData({
stat: res
});
} catch (err) {
console.log(err);
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: function (e) {
pageHelper.url(e, this);
},
bindMoreTap: function (e) {
let itemList = ['取消所有首页推荐'];
wx.showActionSheet({
itemList,
success: async res => {
let idx = res.tapIndex;
if (idx == 0) {
this._clearVouch();
}
},
fail: function (res) { }
})
},
_clearVouch: async function (e) {
let cb = async () => {
try {
await cloudHelper.callCloudSumbit('admin/clear_vouch').then(res => {
pageHelper.showSuccToast('操作成功');
})
} catch (err) {
console.log(err);
}
};
pageHelper.showConfirm('您确认清除所有首页推荐?', cb)
},
bindExitTap: function (e) {
let callback = function () {
AdminBiz.clearAdminToken();
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/my/index/my_index'),
});
}
pageHelper.showConfirm('您确认退出?', callback);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/home/admin_home.json
================================================
{
"usingComponents": {
"cmpt-foot": "/cmpts/biz/foot/foot_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "后台管理"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/home/admin_home.wxml
================================================
{{admin.name}}
超级管理员
一般管理员
共登录{{admin.cnt}}次,上次{{admin.last}}
{{item.cnt||'0'}}{{item.title}}
功能管理
用户管理
内容管理
预约管理
攻略管理
景点管理
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/home/admin_home.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.main-admin {
padding: 0;
}
.admin-info {
height: 200rpx;
width: 100%;
color: #fff;
display: flex;
justify-content: flex-start;
align-items: center;
position: relative;
}
.admin-info .pic {
width: 100rpx;
height: 100rpx;
font-size: 70rpx;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
border-radius:20rpx;
margin-left:20rpx;
}
.admin-info .right {
flex: 1;
display: flex;
flex-direction: column;
}
.admin-info .right .name {
font-size: 32rpx;
line-height: 1.8;
}
.admin-info .right .desc {
font-size: 24rpx;
line-height: 1.3;
}
.admin-info .exit-admin {
position: absolute;
top: 12rpx;
right: 12rpx;
font-size: 45rpx;
}
.main-admin .comm-list,
.main-admin .bar {
background-color: #fff;
width: 100%;
}
.main-admin .exit {
width: 100%;
padding: 0 20rpx;
margin-bottom: 50rpx;
}
.main-admin .exit button {
width: 100%;
color: #fff;
height: 70rpx;
}
.grid.col-6>view {
width: 16.66%;
}
.foot-bottom {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
bottom: 50rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/login/admin_login.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
name: '',
pwd: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
AdminBiz.clearAdminToken();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: function (e) {
pageHelper.url(e, this);
},
bindBackTap: function (e) {
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/my/index/my_index'),
});
},
bindLoginTap: async function (e) {
return AdminBiz.adminLogin(this, this.data.name, this.data.pwd);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/login/admin_login.json
================================================
{
"usingComponents": {
"cmpt-foot": "/cmpts/biz/foot/foot_cmpt"
},
"disableScroll": true,
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/login/admin_login.wxml
================================================
后台管理系统
账号:
密码:
本系统仅限于系统管理员登录
返回用户端
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/index/login/admin_login.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.main-admin {
width: 100%;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
}
.login {
width: 500rpx;
background-color: #fff;
min-height: 550rpx;
border-radius: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 50rpx 20rpx;
margin-top: -100rpx;
}
.login button {
width: 85%;
color: #fff;
font-size: 32rpx;
}
.login .hint {
width: 100%;
color: #000;
font-weight: bold;
font-size: 36rpx;
text-align: center;
}
.login .return {
width: 100%;
font-size: 30rpx;
text-align: center;
font-size: 26rpx;
}
.pic {
width: 180rpx;
height: 180rpx;
font-size: 130rpx;
display: flex;
justify-content: center;
align-items: center;
}
.foot-bottom {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 50rpx;
}
.site-footer {
color: #fff !important;
}
.form-group .title {
color:#333;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/cover/admin_meet_cover.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const dataHelper = require('../../../../../../helper/data_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
title: '',
status: '',
mode: '',
desc: '',
pic: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this)) return;
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
let formStyleSet = parent.data.formStyleSet;
let title = parent.data.formTitle;
let status = parent.data.beginSetDesc;
this.setData({
title,
status,
...formStyleSet
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
url: function (e) {
pageHelper.url(e, this);
},
bindChooseImgTap: function (e) {
let that = this;
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['compressed'], //可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], //从相册选择
success: async (res) => {
let pic = res.tempFiles[0].tempFilePath;
that.setData({
pic
});
}
});
},
catchDelImgTap: function (e) {
let that = this;
let callback = function () {
that.setData({
pic: ''
});
}
pageHelper.showConfirm('确定要删除该图片吗?', callback);
},
bindSaveTap: function (e) {
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
if (!this.data.pic) return pageHelper.showModal('请上传封面图片');
parent.setData({
formStyleSet: {
desc: dataHelper.fmtText(this.data.desc),
pic: this.data.pic
}
});
wx.navigateBack();
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/cover/admin_meet_cover.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "后台-预约封面设置"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/cover/admin_meet_cover.wxml
================================================
简要描述
{{desc.length}}/100
封面图 (必填)
更换图片
上传图片
效果图
{{title||'演示标题'}}
{{desc||'简要描述未填写'}}
{{status||'9天可预约'}}
不保存,返回
保存封面设置
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/cover/admin_meet_cover.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
@import '../../../../../../style/public/comm_box_list.wxss';
.form-box-main {
width: 100%;
box-sizing: border-box;
padding: 0rpx 20rpx;
}
.demo {
background-color: #ffff;
width: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.demo {
background-color: #f2f2f2;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding: 0 20rpx;
}
.demo .demo-title {
width: 100%;
background-color: #fff;
padding: 10rpx 20rpx;
font-size: 32rpx;
color: #333;
text-align: center;
}
.form-group .upload-img {
display: flex;
flex-wrap: wrap;
overflow: hidden;
flex: 1;
}
.form-group .cover-img {
flex: 1;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
}
.form-group .cover-img image {
width: 60rpx;
height: 60rpx;
border-radius: 5rpx;
}
.form-group .cover-img .close-img,
.form-group .cover-img .upload-img {
height: 60rpx;
border-radius: 5rpx;
text-align: center;
display: flex;
justify-content: flex-end;
align-items: center;
font-size: 50rpx;
color: orange;
min-width: 80rpx;
}
.form-group .cover-img .upload-img {
color: #999;
font-weight: normal;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/edit/admin_meet_edit.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const PublicBiz = require('../../../../../../comm/biz/public_biz.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const timeHelper = require('../../../../../../helper/time_helper.js');
const validate = require('../../../../../../helper/validate.js');
const AdminMeetBiz = require('../../../../biz/admin_meet_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
id: null,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
pageHelper.getOptions(this, options);
this.setData(await AdminMeetBiz.initFormData()); // 初始化表单数据
await this._loadDetail();
this._setContentDesc();
},
_loadDetail: async function () {
let id = this.data.id;
if (!id) return;
this.formSetBarTitleByAddEdit(id, '后台-预约');
let params = {
id
};
let opt = {
title: 'bar'
};
let meet = await cloudHelper.callCloudData('admin/meet_detail', params, opt);
if (!meet) {
this.setData({
isLoad: null
})
return;
}
this.setData({
isLoad: true,
// 表单数据
formTitle: meet.MEET_TITLE,
formTypeId: meet.MEET_TYPE_ID,
formContent: meet.MEET_CONTENT,
formOrder: meet.MEET_ORDER,
formStyleSet: meet.MEET_STYLE_SET,
formDaysSet: meet.MEET_DAYS_SET,
formIsShowLimit: meet.MEET_IS_SHOW_LIMIT,
formFormSet: meet.MEET_FORM_SET,
});
},
// 编辑或者添加设置标题
formSetBarTitleByAddEdit(id, title) {
title = id ? title + '编辑' : title + '添加',
wx.setNavigationBarTitle({
title
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
model: function (e) {
pageHelper.model(this, e);
},
bindFormSetCmpt: function (e) {
this.setData({
formFormSet: e.detail,
});
},
bindFormAddSubmit: async function () {
pageHelper.formClearFocus(this);
if (!AdminBiz.isAdmin(this)) return;
let data = this.data;
if (data.formTitle.length <= 0) return pageHelper.formHint(this, 'formTitle', '请填写「标题」');
if (data.formTypeId.length <= 0) return pageHelper.formHint(this, 'formTypeId', '请选择「分类」');
if (data.formStyleSet.pic.length <= 0) {
pageHelper.anchor('formStyleSet', this);
return pageHelper.formHint(this, 'formStyleSet', '封面图片未设置');
}
if (data.formDaysSet.length <= 0) {
pageHelper.anchor('formDaysSet', this);
return pageHelper.formHint(this, 'formDaysSet', '请配置「可预约时段」');
}
if (data.formFormSet.length <= 0) return pageHelper.showModal('请至少设置一项「用户填写资料」');
if (data.contentDesc.includes('未填写'))
return pageHelper.formHint(this, 'formContent', '请填写「详细介绍」');
data = validate.check(data, AdminMeetBiz.CHECK_FORM, this);
if (!data) return;
data.typeName = AdminMeetBiz.getTypeName(data.typeId);
try {
// 先创建,再上传
let result = await cloudHelper.callCloudSumbit('admin/meet_insert', data);
let meetId = result.data.id;
let formContent = this.data.formContent;
if (formContent && formContent.length > 0) {
wx.showLoading({
title: '提交中...',
mask: true
});
let content = await cloudHelper.transRichEditorTempPics(formContent, 'meet/', meetId, 'admin/meet_update_content');
this.setData({
formContent: content
});
}
// 样式 提交处理
let formStyleSet = this.data.formStyleSet;
wx.showLoading({
title: '提交中...',
mask: true
});
if (!await AdminMeetBiz.updateMeetStyleSet(this, meetId, formStyleSet)) return;
let callback = async function () {
PublicBiz.removeCacheList('admin-meet');
PublicBiz.removeCacheList('meet-list');
wx.navigateBack();
}
pageHelper.showSuccToast('添加成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
bindFormEditSubmit: async function () {
pageHelper.formClearFocus(this);
if (!AdminBiz.isAdmin(this)) return;
let data = this.data;
if (data.formTitle.length <= 0) return pageHelper.formHint(this, 'formTitle', '请填写「标题」');
if (data.formTypeId.length <= 0) return pageHelper.formHint(this, 'formTypeId', '请选择「分类」');
if (data.formStyleSet.pic.length <= 0) {
pageHelper.anchor('formStyleSet', this);
return pageHelper.formHint(this, 'formStyleSet', '封面图片未设置');
}
if (data.formDaysSet.length <= 0) {
pageHelper.anchor('formDaysSet', this);
return pageHelper.formHint(this, 'formDaysSet', '请配置「可预约时段」');
}
if (data.formFormSet.length <= 0) return pageHelper.showModal('请至少设置一项「用户填写资料」');
data = validate.check(data, AdminMeetBiz.CHECK_FORM, this);
if (!data) return;
data.typeName = AdminMeetBiz.getTypeName(data.typeId);
try {
let meetId = this.data.id;
data.id = meetId;
// 先修改,再上传
await cloudHelper.callCloudSumbit('admin/meet_edit', data);
// 富文本 提交处理
let formContent = this.data.formContent;
wx.showLoading({
title: '提交中...',
mask: true
});
let content = await cloudHelper.transRichEditorTempPics(formContent, 'meet/', meetId, 'admin/meet_update_content');
this.setData({
formContent: content
});
// 样式 提交处理
let formStyleSet = this.data.formStyleSet;
wx.showLoading({
title: '提交中...',
mask: true
});
if (!await AdminMeetBiz.updateMeetStyleSet(this, meetId, formStyleSet)) return;
let callback = async function () {
// 更新列表页面数据
let node = {
'MEET_TITLE': data.title,
'MEET_TYPE_NAME': data.typeName,
'MEET_DAYS_SET': data.daysSet,
'MEET_FORM_SET': data.formSet,
'MEET_EDIT_TIME': timeHelper.time('Y-M-D h:m:s'),
'leaveDay': AdminMeetBiz.getLeaveDay(data.daysSet)
}
pageHelper.modifyPrevPageListNodeObject(meetId, node);
wx.navigateBack();
}
pageHelper.showSuccToast('编辑成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
bindMyImgUploadListener: function (e) {
this.setData({
imgList: e.detail
});
},
switchModel: function (e) {
pageHelper.switchModel(this, e);
},
url: function (e) {
pageHelper.url(e, this);
},
_setContentDesc: function () {
let contentDesc = '未填写';
let content = this.data.formContent;
let imgCnt = 0;
let textCnt = 0;
for (let k = 0; k < content.length; k++) {
if (content[k].type == 'img') imgCnt++;
if (content[k].type == 'text') textCnt++;
}
if (imgCnt || textCnt) {
contentDesc = textCnt + '段文字,' + imgCnt + '张图片';
}
this.setData({
contentDesc
});
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/edit/admin_meet_edit.json
================================================
{
"usingComponents": {
"cmpt-form-set": "/cmpts/public/form/form_set/form_set_cmpt",
"cmpt-picker-time": "/cmpts/public/picker_time/picker_time_cmpt",
"cmpt-picker-multi": "/cmpts/public/picker_multi/picker_multi_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "后台-预约添加"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/edit/admin_meet_edit.wxml
================================================
标题
{{formTitleFocus}}
分类
{{formTypeIdFocus}}
封面设置(必填)
封面图片未设置
{{formStyleSetFocus}}
详细介绍(必填)
详细介绍未填写
{{contentDesc}}
{{formContentFocus}}
排序号
{{formOrderFocus}}
预约时间设置
{{formDaysSet.length}}天可约
请配置可预约时段
{{formDaysSetFocus}}
是否显示可预约人数
{{formIsShowLimitFocus}}
用户填写资料项目设置 (共{{formFormSet.length}}个字段)
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/edit/admin_meet_edit.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.main-admin {
padding: 20rpx 0rpx;
}
.form-box {
border-radius: 0;
}
.modal-rules {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0;
}
.modal-rules .padding-xl {
padding: 0;
}
.modal-rules .item {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-bottom: 2rpx solid #ddd;
padding: 20rpx 0;
position: relative;
}
.modal-rules .item.cur {
background-color: #f2f2f2;
font-weight: bold;
}
.modal-rules .item.cur .icon-right {
color: var(--adminColor)!important;
}
.modal-rules .item .icon-right {
right: 10rpx;
position: absolute;
top: 50rpx;
}
.modal-rules .item.item-last {
border-bottom: 0;
}
.modal-rules .item .title {
font-size: 36rpx;
color: #111;
}
.modal-rules .item .desc {
font-size: 28rpx;
color: #777;
margin-top: 10rpx;
}
cmpt-picker-multi {
flex: 1;
}
.form-group .desc-textarea {
height: 100rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/export/admin_join_export.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const timeHelper = require('../../../../../../helper/time_helper.js');
const fileHelper = require('../../../../../../helper/file_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
title: '',
url: '',
time: '',
startDay: timeHelper.time('Y-M-D'),
endDay: timeHelper.time('Y-M-D'),
status: 1
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (!pageHelper.getOptions(this, options, 'meetId')) return;
if (options && options.title) {
let title = decodeURIComponent(options.title);
this.setData({
title
});
}
this._loadDetail(1);
},
_loadDetail: async function (isDel) {
if (!AdminBiz.isAdmin(this)) return;
let params = {
isDel
}
let options = {
title: 'bar'
}
let data = await cloudHelper.callCloudData('admin/join_data_get', params, options);
if (!data) return;
this.setData({
isLoad: true,
url: data.url,
time: data.time
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail(1);
wx.stopPullDownRefresh();
},
bindOpenTap:function(e) {
fileHelper.openDoc('预约名单', this.data.url);
},
url: async function (e) {
pageHelper.url(e, this);
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
bindExportTap: async function (e) {
try {
let options = {
title: '数据生成中'
}
let params = {
meetId: this.data.meetId,
startDay: this.data.startDay,
endDay: this.data.endDay,
status: this.data.status
}
await cloudHelper.callCloudData('admin/join_data_export', params, options).then(res => {
this._loadDetail(0);
pageHelper.showModal('数据文件生成成功(' + res.total + '条记录), 请点击「直接打开」按钮或者复制文件地址下载');
});
} catch (err) {
console.log(err);
pageHelper.showNoneToast('导出失败,请重试');
}
},
bindDelTap: async function (e) {
try {
let options = {
title: '数据删除中'
}
await cloudHelper.callCloudData('admin/join_data_del', {}, options).then(res => {
this.setData({
url: '',
time: ''
});
pageHelper.showSuccToast('删除成功');
});
} catch (err) {
console.log(err);
pageHelper.showNoneToast('删除失败,请重试');
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/export/admin_join_export.json
================================================
{
"usingComponents": {
"cmpt-picker-time": "/cmpts/public/picker_time/picker_time_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "预约名单导出"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/export/admin_join_export.wxml
================================================
{{title}}
起始日期
{{startDay||'请选择'}}
终止日期
{{endDay||'请选择'}}
数据类型
数据下载链接({{time}} 生成)
※ 链接使用说明
1. 复制以上链接地址,建议在电脑浏览器中打开链接下载数据文件
2. 为保障信息安全,请勿外传数据链接
3. 为了防止隐私数据泄露,请在下载后及时点击下方按钮删除
为了防止隐私数据泄露,请在下载上述文件后及时点击按钮删除
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/export/admin_join_export.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.form-box .title-desc {
padding-bottom: 10rpx;
border: 0;
font-size: 29rpx;
color: #888;
}
.btn-admin{
margin-bottom: 20rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/join/admin_meet_join.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cacheHelper = require('../../../../../../helper/cache_helper.js');
const helper = require('../../../../../../helper/helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const CACHE_CANCEL_REASON = 'JOIN_CANCEL_REASON';
const CACHE_REFUSE_REASON = 'JOIN_REFUSE_REASON';
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
isAllFold: true,
parentDayIdx: 0,
parentTimeIdx: 0,
menuIdx: 0,
meetId: '',
mark: '',
title: '',
titleEn: '',
cancelModalShow: false,
cancelAllModalShow: false,
refuseModalShow: false,
formReason: '',
curIdx: -1
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this)) return;
// 附加参数
if (options && options.meetId && options.mark) {
//设置搜索菜单
this._getSearchMenu();
this.setData({
meetId: options.meetId,
mark: options.mark,
parentDayIdx: options.dayidx,
parentTimeIdx: options.timeidx,
time: options.time,
_params: {
meetId: options.meetId,
mark: options.mark,
}
}, () => (
this.setData({
isLoad: true
})
));
}
if (options && options.title) {
let title = decodeURIComponent(options.title);
this.setData({
title,
titleEn: options.title
});
wx.setNavigationBarTitle({
title: '分时段预约名单 - ' + title
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindUnFoldTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let dataList = this.data.dataList;
dataList.list[idx].fold = false;
this.setData({
dataList
});
},
bindFoldTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let dataList = this.data.dataList;
dataList.list[idx].fold = true;
this.setData({
dataList
});
},
bindFoldAllTap: function (e) {
let dataList = this.data.dataList;
for (let k = 0; k < dataList.list.length; k++) {
dataList.list[k].fold = true;
}
this.setData({
isAllFold: true,
dataList
});
},
bindUnFoldAllTap: function (e) {
let dataList = this.data.dataList;
for (let k = 0; k < dataList.list.length; k++) {
dataList.list[k].fold = false;
}
this.setData({
isAllFold: false,
dataList
});
},
bindCopyTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let forms = this.data.dataList.list[idx].JOIN_FORMS;
let ret = '';
ret += `项目:${this.data.dataList.list[idx].JOIN_MEET_TITLE}\r`;
ret += `时段:${this.data.dataList.list[idx].JOIN_MEET_DAY} ${this.data.dataList.list[idx].JOIN_MEET_TIME_START}~${this.data.dataList.list[idx].JOIN_MEET_TIME_END}\r`;
for (let k = 0; k < forms.length; k++) {
ret += forms[k].title + ':' + forms[k].val + '\r';
}
wx.setClipboardData({
data: ret,
success(res) {
wx.getClipboardData({
success(res) {
pageHelper.showSuccToast('已复制到剪贴板');
}
})
}
});
},
bindCancelTap: function (e) {
this.setData({
formReason: cacheHelper.get(CACHE_CANCEL_REASON) || '',
curIdx: pageHelper.dataset(e, 'idx'),
cancelModalShow: true
});
},
bindCancelAllTap: function (e) {
this.setData({
formReason: '',
cancelAllModalShow: true
});
},
bindRefuseTap: function (e) {
this.setData({
formReason: cacheHelper.get(CACHE_REFUSE_REASON) || '',
curIdx: pageHelper.dataset(e, 'idx'),
refuseModalShow: true
});
},
bindCancelCmpt: async function () {
let e = {
currentTarget: {
dataset: {
status: 99,
idx: this.data.curIdx
}
}
}
cacheHelper.set(CACHE_CANCEL_REASON, this.data.formReason, 86400 * 365);
await this.bindStatusTap(e);
},
bindCancelAllCmpt: async function () {
try {
let params = {
reason: this.data.formReason,
meetId: this.data.meetId,
timeMark: this.data.mark
}
let opt = {
title: '预约记录取消中'
}
await cloudHelper.callCloudSumbit('admin/meet_cancel_time_join', params, opt).then(res => {
let callback = () => {
let parent = pageHelper.getPrevPage(2);
if (parent) {
let daysSet = parent.data.daysSet;
daysSet[this.data.parentDayIdx].times[this.data.parentTimeIdx].stat = res.data;
parent.setData({
daysSet
});
}
wx.redirectTo({
url: `admin_meet_join?meetId=${this.data.meetId}&mark=${this.data.mark}&title=${this.data.titleEn}&time=${this.data.time}&dayidx=${this.data.parentDayIdx}&timeidx=${this.data.parentTimeIdx}`,
});
}
pageHelper.showSuccToast('取消成功', 1500, callback);
})
} catch (err) {
console.log(err);
};
},
bindRefuseCmpt: async function () {
let e = {
currentTarget: {
dataset: {
status: 8,
idx: this.data.curIdx
}
}
}
cacheHelper.set(CACHE_REFUSE_REASON, this.data.formReason, 86400 * 365);
await this.bindStatusTap(e);
},
bindCheckinTap: async function (e) {
let flag = Number(pageHelper.dataset(e, 'flag'));
let callback = async () => {
let idx = Number(pageHelper.dataset(e, 'idx'));
let dataList = this.data.dataList;
let joinId = dataList.list[idx]._id;
let params = {
joinId,
flag,
}
let opts = {
title: '处理中'
}
try {
await cloudHelper.callCloudSumbit('admin/join_checkin', params, opts).then(res => {
let cb = () => {
let sortIndex = this.selectComponent('#cmpt-comm-list').getSortIndex();
if (sortIndex >= 10 && !this.data.search) { // 全部或者检索的结果
dataList.list.splice(idx, 1);
dataList.total--;
} else {
dataList.list[idx].JOIN_IS_CHECKIN = flag;
}
this.setData({
dataList
});
}
pageHelper.showSuccToast('操作成功', 1000, cb);
});
} catch (err) {
console.error(err);
}
}
if (flag == 1)
pageHelper.showConfirm('确认「签到核销」?', callback);
else if (flag == 0)
pageHelper.showConfirm('确认「取消签到」?', callback);
},
bindDelTap: async function (e) {
let callback = async () => {
let idx = Number(pageHelper.dataset(e, 'idx'));
let dataList = this.data.dataList;
let joinId = dataList.list[idx]._id;
let params = {
joinId
}
let opts = {
title: '删除中'
}
try {
await cloudHelper.callCloudSumbit('admin/join_del', params, opts).then(res => {
let cb = () => {
let dataList = this.data.dataList;
dataList.list.splice(idx, 1);
dataList.total--;
this.setData({
dataList
});
let parent = pageHelper.getPrevPage(2);
if (parent) {
let daysSet = parent.data.daysSet;
daysSet[this.data.parentDayIdx].times[this.data.parentTimeIdx].stat = res.data;
parent.setData({
daysSet
});
}
}
pageHelper.showSuccToast('删除成功', 1000, cb);
});
} catch (err) {
console.error(err);
}
}
pageHelper.showConfirm('确认删除该预约记录? 删除后用户将无法查询到本预约记录', callback);
},
bindStatusTap: async function (e) {
let status = Number(pageHelper.dataset(e, 'status'));
let oldStatus = Number(pageHelper.dataset(e, 'old'));
let callback = async () => {
let idx = Number(pageHelper.dataset(e, 'idx'));
let dataList = this.data.dataList;
let joinId = dataList.list[idx]._id;
let params = {
joinId,
status,
reason: this.data.formReason
}
let opts = {
title: '处理中'
}
try {
await cloudHelper.callCloudSumbit('admin/join_status', params, opts).then(res => {
pageHelper.showSuccToast('操作成功', 1000);
let sortIndex = this.selectComponent('#cmpt-comm-list').getSortIndex();
if (sortIndex != -1 && sortIndex != 5 && !this.data.search) { // 全部或者检索的结果
dataList.list.splice(idx, 1);
dataList.total--;
} else {
dataList.list[idx].JOIN_REASON = this.data.formReason;
dataList.list[idx].JOIN_STATUS = status;
dataList.list[idx].JOIN_IS_CHECKIN = 0;
}
this.setData({
cancelModalShow: false,
refuseModalShow: false,
formReason: '',
curIdx: -1,
dataList
});
let parent = pageHelper.getPrevPage(2);
if (parent) {
let daysSet = parent.data.daysSet;
daysSet[this.data.parentDayIdx].times[this.data.parentTimeIdx].stat = res.data;
parent.setData({
daysSet
});
}
});
} catch (err) {
console.error(err);
}
}
switch (status) {
case 99: //直接取消
await callback();
break;
case 1: {
if (oldStatus == 10)
pageHelper.showConfirm('确认变更为「预约成功」?', callback);
else if (oldStatus == 99)
pageHelper.showConfirm('确认恢复为「预约成功」状态?', callback);
break;
}
}
},
bindCommListCmpt: function (e) {
if (helper.isDefined(e.detail.search))
this.setData({
search: '',
sortType: '',
});
else {
let dataList = e.detail.dataList;
if (dataList) {
for (let k = 0; k < dataList.list.length; k++) {
dataList.list[k].fold = this.data.isAllFold;
}
}
this.setData({
dataList,
});
if (e.detail.sortType)
this.setData({
sortType: e.detail.sortType,
});
}
},
// 修改与展示状态菜单
_getSearchMenu: function () {
let sortItems = [];
let sortMenus = [{
label: '全部',
type: '',
value: ''
}, {
label: `成功`,
type: 'status',
value: 1
},
{
label: `已取消`,
type: 'status',
value: 1099
},
{
label: `已签到`,
type: 'checkin',
value: 1
},
{
label: `未签到`,
type: 'checkin',
value: 0
}
]
this.setData({
sortItems,
sortMenus
})
},
bindClearReasonTap: function (e) {
this.setData({
formReason: ''
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/join/admin_meet_join.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "分时段预约名单"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/join/admin_meet_join.wxml
================================================
({{time}})
共有{{dataList.total}}条符合条件记录
全部展开
全部收起
状态
:
预约成功
,已签到
,未签到
用户取消
系统取消
取消原因
:
{{item.JOIN_REASON||'未填'}}
未通过原因
:
{{item.JOIN_REASON||'未填'}}
{{form.title}}
:
{{valItem}}
{{valItem}}
更多资料...
收起
提交/变更
:
{{item.JOIN_EDIT_TIME}}
签到核销
取消签到
取消预约
恢复预约
删除
{{index+1}}
取消理由 (选填):
清空
取消理由 (选填):
清空
不通过理由 (选填):
清空
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/join/admin_meet_join.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
page {
background-color: #f8f8f8;
}
.join-list {
width: 100%;
padding: 0rpx 20rpx 20rpx;
flex-direction: column;
align-items: center;
justify-content: flex-start;
position: relative;
}
.join-list .item {
width: 100%;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #fff;
border-radius: 5rpx;
overflow: hidden;
margin-bottom: 30rpx;
position: relative;
border: 1rpx solid #ddd;
}
.join-list .item .no {
position: absolute;
right: 0;
bottom: 0;
font-size: 25rpx;
color: #999;
line-height: 2;
padding: 0rpx 15rpx 0rpx 15rpx;
background-color: #eee;
border-top-left-radius: 15rpx;
}
.join-list .item .header {
width: 100%;
font-size: 26rpx;
text-align: left;
line-height: 2.6;
background-color: #f2f2f2;
padding: 0 15rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.join-list .item .header .left {
color: #333;
}
.join-list .item .header .right {
font-size: 25rpx;
color: #666;
}
.join-list .item .info {
width: 100%;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #fff;
min-height: 150rpx;
padding: 15rpx 20rpx 20rpx;
position: relative;
}
.join-list .item .info .right-corner {
position: absolute;
left: 10rpx;
top: 0;
width: 100rpx;
height: 50rpx;
}
.join-list .item .info .fold {
width: 100%;
text-align: center;
font-size: 24rpx;
color: #999;
padding: 0 20rpx 10rpx;
}
.join-list .item .info .info-item {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: flex-start;
background-color: #fff;
line-height: 1.5;
font-size: 25rpx;
font-weight: bold;
margin-bottom: 20rpx;
}
.join-list .item .info .info-item .title {
width: 160rpx;
color: #777;
text-align: right;
}
.join-list .item .info .info-item .mao {
color: #777;
text-align: left;
margin-right: 10rpx;
}
.join-list .item .info .info-item .content {
flex: 1;
color: #333;
display: flex;
justify-content: flex-start;
align-items: flex-start;
}
.join-list .item .info .info-item.add-time {
font-size: 24rpx !important;
font-weight: normal !important;
border-top: 1rpx dashed #ccc;
padding-top: 15rpx;
}
.join-list .item .info .oprt {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 0rpx 0 0rpx;
}
.join-list .item .info .oprt .btn {
padding: 0 30rpx;
font-size: 26rpx;
border-radius: 10rpx !important;
}
.cancel-area {
border: 1rpx solid #ddd;
}
.time-line {
width: 100%;
text-align: right;
font-size: 24rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/list/admin_meet_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const AdminMeetBiz = require('../../../../biz/admin_meet_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
//设置搜索菜单
await this._getSearchMenu();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
bindScanTap: function (e) {
let meetId = pageHelper.dataset(e, 'id');
let title = encodeURIComponent(pageHelper.dataset(e, 'title'));
wx.navigateTo({
url: '../scan/admin_meet_scan?meetId=' + meetId + '&title=' + title,
});
},
bindRecordSelectTap: async function (e) {
let itemList = ['预约名单', '导出名单Excel文件', '管理员核销预约码', '用户自助签到码'];
let meetId = pageHelper.dataset(e, 'id');
let title = encodeURIComponent(pageHelper.dataset(e, 'title'));
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //预约名单
wx.navigateTo({
url: '../record/admin_record_list?meetId=' + meetId + '&title=' + title,
});
break;
}
case 1: { //导出
wx.navigateTo({
url: '../export/admin_join_export?meetId=' + meetId + '&title=' + title,
});
break;
}
case 2: { //核验
this.bindScanTap(e);
break;
}
case 3: { //自助签到码
pageHelper.showModal('请进入「名单与核销->预约名单->名单」, 查看某一时段的「用户自助签到码」');
break;
}
}
},
fail: function (res) { }
})
},
_setSort: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let meetId = pageHelper.dataset(e, 'id');
let sort = pageHelper.dataset(e, 'sort');
if (!meetId) return;
let params = {
meetId,
sort
}
try {
await cloudHelper.callCloudSumbit('admin/meet_sort', params).then(res => {
pageHelper.modifyListNode(meetId, this.data.dataList.list, 'MEET_ORDER', sort);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_setVouch: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let vouch = pageHelper.dataset(e, 'vouch');
if (!id) return;
let params = {
id,
vouch
}
try {
await cloudHelper.callCloudSumbit('admin/meet_vouch', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'MEET_VOUCH', vouch);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
bindMoreSelectTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let idx = pageHelper.dataset(e, 'idx');
let order = this.data.dataList.list[idx].MEET_ORDER;
let orderDesc = (order == 0) ? '取消置顶' : '置顶';
let vouch = this.data.dataList.list[idx].MEET_VOUCH;
let vouchDesc = (vouch == 0) ? '推荐到首页' : '取消首页推荐';
let itemList = ['预览', orderDesc, vouchDesc, '生成专属二维码'];
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //预览
let meetId = pageHelper.dataset(e, 'id');
wx.navigateTo({
url: pageHelper.fmtURLByPID('/pages/meet/detail/meet_detail?id=' + meetId),
});
break;
}
case 1: { //置顶
let sort = (order == 0) ? 9999 : 0;
e.currentTarget.dataset['sort'] = sort;
await this._setSort(e);
break;
}
case 2: { //上首页
vouch = (vouch == 0) ? 1 : 0;
e.currentTarget.dataset['vouch'] = vouch;
await this._setVouch(e);
break;
}
case 3: { //二维码
let title = encodeURIComponent(pageHelper.dataset(e, 'title'));
let qr = encodeURIComponent(pageHelper.dataset(e, 'qr'));
wx.navigateTo({
url: `../../setup/qr/admin_setup_qr?title=${title}&qr=${qr}`,
})
break;
}
}
},
fail: function (res) { }
})
},
bindStatusSelectTap: async function (e) {
let itemList = ['启用', '停止预约 (用户可见)', '关闭 (用户不可见)', '删除'];
let meetId = pageHelper.dataset(e, 'id');
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //启用
await this._setStatus(this, meetId, 1);
break;
}
case 1: { //停止预约
await this._setStatus(this, meetId, 9);
break;
}
case 2: { //关闭
await this._setStatus(this, meetId, 10);
break;
}
case 3: { //删除
await this._del(this, meetId);
break;
}
}
},
fail: function (res) { }
})
},
_del: async function (that, meetId) {
if (!AdminBiz.isAdmin(this)) return;
if (!meetId) return;
let params = {
meetId
}
let callback = async function () {
try {
let opts = {
title: '删除中'
}
await cloudHelper.callCloudSumbit('admin/meet_del', params, opts).then(res => {
pageHelper.delListNode(meetId, that.data.dataList.list, '_id');
that.data.dataList.total--;
that.setData({
dataList: that.data.dataList
});
pageHelper.showSuccToast('删除成功');
});
} catch (e) {
console.log(e);
}
}
pageHelper.showConfirm('确认删除?删除不可恢复', callback);
},
_setStatus: async function (that, meetId, status) {
if (!AdminBiz.isAdmin(this)) return;
if (!meetId) return;
let params = {
meetId,
status
}
try {
await cloudHelper.callCloudSumbit('admin/meet_status', params).then(res => {
pageHelper.modifyListNode(meetId, that.data.dataList.list, 'MEET_STATUS', status, '_id');
that.setData({
dataList: that.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (e) {
console.log(e);
}
},
_getSearchMenu: async function () {
let sortItem1 = [{ label: '分类', type: '', value: '' }];
sortItem1 = sortItem1.concat(AdminMeetBiz.getTypeList());
let sortItems = [sortItem1];
let sortMenus = [{
label: '全部',
type: '',
value: ''
}, {
label: '使用中',
type: 'status',
value: 1
},
{
label: '已停止',
type: 'status',
value: 9
},
{
label: '已关闭',
type: 'status',
value: 10
},
];
this.setData({
sortItems,
sortMenus
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/list/admin_meet_list.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"disableScroll": true,
"navigationBarTitleText": "预约管理"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/list/admin_meet_list.wxml
================================================
共有{{dataList.total}}条符合条件记录
{{index+1}}
状态
:
未启用
使用中
停止 (用户可见)
已关闭 (用户不可见)
分类
:
【{{item.MEET_TYPE_NAME}}】
时段
:
{{item.leaveDay}}天可用
变更
:
{{item.MEET_EDIT_TIME}}
设置
名单与核销
状态
更多..
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/list/admin_meet_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
@import '../../../../../../style/project/admin_list_style.wxss';
page {
background-color: #f8f8f8;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/record/admin_record_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const timeHelper = require('../../../../../../helper/time_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
now: timeHelper.time('Y-M-D'),
searchDayStart: '',
searchDayEnd: '',
daysSet: null,
title: '',
titleEn: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!pageHelper.getOptions(this, options, 'meetId')) return;
if (!AdminBiz.isAdmin(this)) return;
let searchDayStart = timeHelper.time('Y-M-D');
let searchDayEnd = timeHelper.time('Y-M-D');
this.setData({
searchDayStart,
searchDayEnd
}, () => {
this._loadDetail();
});
if (options && options.title) {
let title = decodeURIComponent(options.title);
this.setData({
title,
titleEn: options.title
});
wx.setNavigationBarTitle({
title: '预约名单统计 - ' + title
});
}
},
_loadDetail: async function () {
let meetId = this.data.meetId;
if (!meetId) return;
let params = {
meetId,
start: this.data.searchDayStart,
end: this.data.searchDayEnd
};
let opt = {
title: 'bar'
};
try {
this.setData({
daysSet: null
});
await cloudHelper.callCloudSumbit('admin/meet_day_list', params, opt).then(res => {
this.setData({
isLoad: true,
daysSet: res.data
});
});
} catch (err) {
console.error(err);
}
},
bindSearchTomorrowTap: function (e) {
this.setData({
searchDayStart: timeHelper.time('Y-M-D', 86400),
searchDayEnd: timeHelper.time('Y-M-D', 86400),
}, () => {
this._loadDetail();
});
},
bindSearchYesterdayTap: function (e) {
this.setData({
searchDayStart: timeHelper.time('Y-M-D', -86400),
searchDayEnd: timeHelper.time('Y-M-D', -86400),
}, () => {
this._loadDetail();
});
},
bindSearchTodayTap: function (e) {
this.setData({
searchDayStart: timeHelper.time('Y-M-D'),
searchDayEnd: timeHelper.time('Y-M-D'),
}, () => {
this._loadDetail();
});
},
onPageScroll: function (e) {
if (e.scrollTop > 100) {
this.setData({
topShow: true
});
} else {
this.setData({
topShow: false
});
}
},
bindTopTap: function () {
wx.pageScrollTo({
scrollTop: 0
})
},
bindSearchTap: function (e) {
this._loadDetail();
},
url: function (e) {
pageHelper.url(e, this);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/record/admin_record_list.json
================================================
{
"usingComponents": {
"cmpt-picker-time": "/cmpts/public/picker_time/picker_time_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "预约名单统计"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/record/admin_record_list.wxml
================================================
起始日期:
{{searchDayStart}}
结束日期:
{{searchDayEnd}}
昨日
今日
明日
搜索
时段
已预约/名额
取消
查看
{{item.day}}(已过期)(今日)
{{itm.start}}~{{itm.end}}
{{itm.stat.succCnt}}/{{itm.isLimit?itm.limit:'不限'}}
{{itm.stat.cancelCnt+itm.stat.adminCancelCnt}}
名单
数据加载中...
没有数据哦~
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/record/admin_record_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
@import '../../../../../../style/base/table.wxss';
.main-admin {
width: 100%;
box-sizing: border-box;
padding: 0rpx 0rpx 30rpx;
}
.table .table-form .oprt .min-btn {
padding: 5rpx 30rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/scan/admin_meet_scan.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: true,
title: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (!pageHelper.getOptions(this, options, 'meetId')) return;
if (options && options.title) {
let title = decodeURIComponent(options.title);
this.setData({
title
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
bindScanTap: function (e) {
let meetId = this.data.meetId;
wx.scanCode({
async success(res) {
console.log(res)
if (!res ||
!res.result ||
!res.result.includes('meet=') ||
res.result.length != 20) {
pageHelper.showModal('错误的预约码,请重新扫码');
return;
}
let code = res.result.replace('meet=', '');
let params = {
meetId,
code
};
let options = {
title: '预约码核销中'
}
await cloudHelper.callCloudSumbit('admin/join_scan', params, options).then(res => {
pageHelper.showModal('核销成功');
}).catch(err => {
console.log(err);
});
},
fail(err) {
if (err && err.errMsg == 'scanCode:fail')
pageHelper.showModal('预约码核销错误,请重新扫码');
}
});
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/scan/admin_meet_scan.json
================================================
{
"usingComponents": {
"cmpt-picker-time": "/cmpts/public/picker_time/picker_time_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "管理员扫码核销"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/scan/admin_meet_scan.wxml
================================================
{{title}}
扫码核销
管理员扫描用户预约码进行签到核销
立即核销
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/scan/admin_meet_scan.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.form-box .checkin {
width: 100%;
padding: 40rpx 40rpx 80rpx;
padding-bottom: 150rpx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.form-box .checkin .notice {
width: 100%;
font-size: 46rpx;
text-align: center;
color: #000;
margin-bottom: 30rpx;
}
.form-box .checkin .desc {
width: 100%;
font-size: 32rpx;
text-align: center;
color: #666;
}
.form-box .checkin .oprt {
width: 600rpx;
margin-top: 40rpx;
text-align: center;
font-size: 34rpx;
color: var(--adminColor);
border: 2rpx solid var(--adminColor);
padding: 30rpx 20rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/self/admin_meet_self.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
qrUrl: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (!pageHelper.getOptions(this, options, 'mark'));
if (options && options.title) {
let title = decodeURIComponent(options.title);
this.setData({
title
});
}
await this._loadDetail();
},
_loadDetail: async function () {
let timeMark = this.data.mark;
let page = pageHelper.fmtURLByPID("/pages/meet/self/meet_self");
let params = {
timeMark,
page
};
let opt = {
title: 'bar'
};
try {
await cloudHelper.callCloudSumbit('admin/self_checkin_qr', params, opt).then(res => {
this.setData({
qrUrl: res.data,
isLoad: true
})
});
} catch (err) {
console.error(err);
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
url: function (e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/self/admin_meet_self.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "用户自助签到码"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/self/admin_meet_self.wxml
================================================
{{title}}
用户自助核销/签到
※ 签到规则
1. 预约用户自行扫描下方小程序码完成核销/签到
2. 用户在预约当天扫码有效,不可提前核销
3. 在预约结束时间未到情况下,用户可自行扫码完成核销
4. 若结束时间已到,需要管理员进行人工扫码核销或者在后台核销
长按图片保存小程序码
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/self/admin_meet_self.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.form-box .checkin {
width: 100%;
padding: 40rpx 40rpx 80rpx;
padding-bottom: 150rpx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.form-box .checkin .notice {
width: 100%;
font-size: 46rpx;
text-align: center;
color: #000;
margin-bottom: 30rpx;
}
.form-box .checkin .desc {
width: 100%;
font-size: 28rpx;
text-align: left;
color: #666;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
}
.form-box .checkin image {
width: 500rpx;
height: 500rpx;
margin: 30rpx 0rpx;
}
.form-box .checkin .oprt {
margin-top: 0rpx;
text-align: center;
font-size: 30rpx;
color: var(--adminColor);
padding: 20rpx 10rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/temp/admin_temp_select.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const AdminMeetBiz = require('../../../../biz/admin_meet_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
temps: [],
curIdx: -1,
curTimeModalShow: false,
curTimeIsLimit: false, // 当前操作是否限制人数
curTimeLimit: 50, // 当前时段人数限制
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
await this._loadList();
},
switchModel: function (e) {
pageHelper.switchModel(this, e, 'bool');
},
bindAllLimitSetCmpt: async function (e) {
if (this.data.curIdx <= -1) return;
let temp = this.data.temps[this.data.curIdx];
try {
let opts = {
title: '批量修改中'
}
let params = {
id: temp._id,
limit: this.data.curTimeLimit,
isLimit: this.data.curTimeIsLimit
}
await cloudHelper.callCloudSumbit('admin/meet_temp_edit', params, opts).then(res => {
this.setData({
temps: res.data,
curTimeModalShow: false,
curTimeIsLimit: false,
curTimeLimit: 50,
});
pageHelper.showSuccToast('修改成功');
})
} catch (err) {
console.log(err);
};
},
_loadList: async function () {
try {
let opts = {
title: 'bar'
}
await cloudHelper.callCloudSumbit('admin/meet_temp_list', {}, opts).then(res => {
this.setData({
isLoad: res.data.length == 0 ? null : true,
temps: res.data
})
})
} catch (err) {
console.log(err);
};
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
onPullDownRefresh: async function () {
await this._loadList();
wx.stopPullDownRefresh();
},
bindSelectTap: function (e) {
let curIdx = pageHelper.dataset(e, 'idx');
let temps = this.data.temps[curIdx].TEMP_TIMES;
let name = this.data.temps[curIdx].TEMP_NAME;
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
let days = parent.data.days;
let day = days[parent.data.curIdx].day;
let callback = () => {
let times = [];
for (let k = 0; k < temps.length; k++) {
let node = AdminMeetBiz.getNewTimeNode(day);
node.start = temps[k].start;
node.end = temps[k].end;
node.limit = temps[k].limit;
node.isLimit = temps[k].isLimit;
times.push(node);
}
days[parent.data.curIdx].times = times;
parent.setData({
days
});
wx.navigateBack();
}
pageHelper.showConfirm('确认要选用模板 「' + name + '」配置到日期 「' + day + '」下吗?', callback);
},
_delTemp: async function (curIdx, id) {
try {
let opts = {
title: '删除中'
}
let params = {
id
}
await cloudHelper.callCloudSumbit('admin/meet_temp_del', params, opts).then(res => {
let temps = this.data.temps;
temps.splice(curIdx, 1);
this.setData({
temps
});
if (temps.length == 0) {
this.setData({
isLoad: null
});
}
})
} catch (err) {
console.log(err);
};
},
bindOprtTap: function (e) {
let curIdx = pageHelper.dataset(e, 'idx');
let itemList = ['删除模板', '批量设置人数上限'];
wx.showActionSheet({
itemList,
success: async res => {
let idx = res.tapIndex;
if (idx == 0) { // 删除
let temps = this.data.temps;
let name = temps[curIdx].TEMP_NAME;
let callback = () => {
this._delTemp(curIdx, temps[curIdx]._id);
}
pageHelper.showConfirm('确认要删除模板 「' + name + '」吗?', callback);
}
if (idx == 1) {
this.setData({
curIdx,
curTimeModalShow: true
});
}
},
fail: function (res) { }
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/temp/admin_temp_select.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "时间模板选择"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/temp/admin_temp_select.wxml
================================================
{{item.TEMP_NAME}}
{{timesItem.start}}~{{timesItem.end}}
{{timesItem.limit}}人
不限人数
统一设置该模板下各时段可约人数
是否限制人数
人数上限
人
不限制人数
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/temp/admin_temp_select.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
@import "../../../../../../style/public/article_list.wxss";
.load.notexist::after {
content: "您还没有可选用的模板哦";
}
.text-pic-list-box .item {
padding: 18rpx 30rpx;
position: relative;
}
.item .time-list {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: flex-start;
flex-wrap: wrap;
margin-top: 20rpx;
}
.item .time-list .time-item {
width: 33.33%;
padding: 10rpx;
}
.item .time-list .time-item .detail {
width: 100%;
border: 1rpx solid #ccc;
border-radius: 1rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 13rpx 0;
height: 130rpx;
}
.item .time-list .time-item .detail>text {
font-size: 30rpx;
color: #555;
width: 100%;
text-align: center;
}
.item .time-list .time-item .detail .up {
font-size: 29rpx;
}
.item .detail.select {
font-size: 35rpx;
background-color: #f8f8f8;
color:#777;
}
.item .title {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
color:#333!important;
margin-left:15rpx;
}
.item .title .temp-name {
flex: 1;
}
.item .title .more{
color:#777;
width:60rpx;
text-align: right;
font-size:40rpx;
font-weight: normal;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/time/admin_meet_time.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const dataHelper = require('../../../../../../helper/data_helper.js');
const timeHelper = require('../../../../../../helper/time_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const AdminMeetBiz = require('../../../../biz/admin_meet_biz.js');
const projectSetting = require('../../../../public/project_setting.js');
Page({
/**
* 页面的初始数据
*/
data: {
daysTimeOptions: AdminMeetBiz.getDaysTimeOptions(),
multiDoDay: [], //当前选择
hasDays: [], //超时有数据(simple)
lastHasDays: [], //超时有数据(full)
hasJoinDays: [], //未超时有预约
days: [
/*{
day: '2021-12-11',
dayDesc: '12月11日 (周五)',
times: [{
mark: '',
start: '10:15', //开始
end: '23:59', // 结束
limit: 50, //人数限制
isLimit: false,
}]
}, {
day: '2022-01-11',
dayDesc: '1月11日 (周日)',
times: [{
mark: '',
start: '00:00', //开始
end: '23:59', // 结束
limit: 89, //人数限制
isLimit: true
}]
}*/
],
curIdx: -1, // 当前操作的日子索引
curTimesIdx: -1, // 当前操作的时段索引
curTimeLimitModalShow: false,
curTimeIsLimit: false, // 当前操作是否限制人数
curTimeLimit: 50, // 当前时段人数限制
saveTempModalShow: false,
formTempName: '',
cancelModalShow: false, //删除对话框
formReason: '', //取消理由
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
let parent = pageHelper.getPrevPage(2);
if (parent) {
let formDaysSet = parent.data.formDaysSet;
let days = [];
let lastHasDays = [];
let hasJoinDays = [];
let now = timeHelper.time('Y-M-D');
for (let k = 0; k < formDaysSet.length; k++) { //已超时无法编辑, 有数据显示form
if (formDaysSet[k].day < now)
lastHasDays.push(formDaysSet[k]);
else {
days.push(formDaysSet[k]);
if (this._checkHasJoinCnt(formDaysSet[k].times))
hasJoinDays.push(formDaysSet[k].day);
}
}
this.setData({
hasDays: dataHelper.getArrByKey(lastHasDays, 'day'),
lastHasDays,
hasJoinDays,
days
});
this._syncCalData();
}
},
_setHasJoinDays: function () {
let days = this.data.days;
let now = timeHelper.time('Y-M-D');
let hasJoinDays = [];
for (let k = 0; k < days.length; k++) {
if (days[k].day < now)
continue;
else {
if (this._checkHasJoinCnt(days[k].times))
hasJoinDays.push(days[k].day);
}
}
this.setData({
hasJoinDays
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {},
model: function (e) {
pageHelper.model(this, e);
},
// 判断含有预约的日期
_checkHasJoinCnt: function (times) {
if (!times) return false;
for (let k = 0; k < times.length; k++) {
if (times[k].stat.succCnt || times[k].stat.waitCheckCnt) return true;
}
return false;
},
_syncCalData: function (e) { // 同步日历选中
let days = this.data.days;
let multiDoDay = dataHelper.getArrByKey(days, 'day');
this.setData({
multiDoDay,
});
},
bindTimeAddTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let days = this.data.days;
if (days[idx].times.length >= 20) return pageHelper.showModal('最多可以添加20个时段');
days[idx].times.push(AdminMeetBiz.getNewTimeNode(days[idx].day));
this.setData({
days
});
},
bindCancelMeetJoinCmpt: async function (e) { //取消已有预约
let curIdx = this.data.curIdx;
let curTimesIdx = this.data.curTimesIdx;
let days = this.data.days;
try {
let parent = pageHelper.getPrevPage(2);
if (!parent) return;
let params = {
reason: this.data.formReason,
meetId: parent.data.id,
timeMark: days[curIdx].times[curTimesIdx].mark
}
let opt = {
title: '预约记录取消中'
}
await cloudHelper.callCloudSumbit('admin/meet_cancel_time_join', params, opt).then(res => {
let callback = () => {
days[curIdx].times.splice(curTimesIdx, 1);
this.setData({
days,
cancelModalShow: false,
formReason: ''
});
this._setHasJoinDays();
}
pageHelper.showSuccToast('取消成功', 1500, callback);
})
} catch (err) {
console.log(err);
};
},
bindTimeDelTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let timesIdx = pageHelper.dataset(e, 'timesidx');
let days = this.data.days;
let node = days[idx].times[timesIdx];
if (node.stat.succCnt || node.stat.waitCheckCnt) {
let callback = async () => {
this.setData({
formReason: '',
curIdx: idx,
curTimesIdx: timesIdx,
cancelModalShow: true //显示对话框
});
};
pageHelper.showConfirm('该时段已有「' + (node.stat.succCnt + node.stat.waitCheckCnt) + '人」预约/预约待审核,若选择删除则将取消所有预约,请仔细确认! 若不想取消,可以选择停止该时段', callback);
} else {
let callback = () => {
days[idx].times.splice(timesIdx, 1);
this.setData({
days
});
};
pageHelper.showConfirm('是否要删除该时间段?', callback);
}
},
bindTimeStatusSwitch: function (e) {
let idx = pageHelper.dataset(e, 'idx');
let timesIdx = pageHelper.dataset(e, 'timesidx');
let days = this.data.days;
let status = days[idx].times[timesIdx].status;
if (status == 0) {
days[idx].times[timesIdx].status = 1;
this.setData({
days
});
} else {
let yes = () => {
days[idx].times[timesIdx].status = 0;
this.setData({
days
});
};
pageHelper.showConfirm('是否要停止该时间段的预约?停止后,已有预约记录仍将保留', yes);
}
},
bindDaysTimeStartCmpt: function (e) {
let start = e.detail.join(':');
let idx = pageHelper.dataset(e, 'idx');
let timesIdx = pageHelper.dataset(e, 'timesidx');
let days = this.data.days;
let end = days[idx].times[timesIdx].end;
if (start >= end) return pageHelper.showModal('开始时间不能大于等于结束时间');
days[idx].times[timesIdx].start = start;
this.setData({
days
});
},
bindDaysTimeEndCmpt: function (e) {
let end = e.detail.join(':');
let idx = pageHelper.dataset(e, 'idx');
let timesIdx = pageHelper.dataset(e, 'timesidx');
let days = this.data.days;
let start = days[idx].times[timesIdx].start;
if (start >= end) return pageHelper.showModal('开始时间不能大于等于结束时间');
days[idx].times[timesIdx].end = end;
this.setData({
days
});
},
switchModel: function (e) {
pageHelper.switchModel(this, e, 'bool');
},
bindSaveTempCmpt: async function (e) {
try {
let name = this.data.formTempName;
if (name.length <= 0) return pageHelper.showNoneToast('请填写模板名称');
if (name.length > 20) return pageHelper.showNoneToast('模板名称不能超过20个字哦');
let days = this.data.days;
let times = days[this.data.curIdx].times;
if (times.length <= 0) return pageHelper.showNoneToast('至少需要包含一个时段');
if (times.length > 20) return pageHelper.showNoneToast('时段不能超过20个');
let temps = [];
for (let k = 0; k < times.length; k++) {
let node = {};
node.start = times[k].start;
node.end = times[k].end;
node.isLimit = times[k].isLimit;
node.limit = times[k].limit;
temps.push(node);
}
let opt = {
title: '模板保存中'
}
let params = {
name,
times: temps
}
await cloudHelper.callCloudSumbit('admin/meet_temp_insert', params, opt).then(res => {
pageHelper.showSuccToast('保存成功');
this.setData({
saveTempModalShow: false,
formTempName: '',
});
})
} catch (err) {
console.log(err);
};
},
bindTimeLimitSetCmpt: function (e) {
let days = this.data.days;
let idx = this.data.curIdx;
let timesIdx = this.data.curTimesIdx;
if (this.data.curTimesIdx == -1) {
// 全天
for (let k = 0; k < days[idx].times.length; k++) {
days[idx].times[k].isLimit = this.data.curTimeIsLimit;
days[idx].times[k].limit = this.data.curTimeLimit;
}
} else {
// 某时间段
let node = days[idx].times[timesIdx];
node.isLimit = this.data.curTimeIsLimit;
node.limit = this.data.curTimeLimit;
days[idx].times[timesIdx] = node;
}
this.setData({
days,
curTimeLimitModalShow: false
});
},
bindShowTimeLimitModalTap: function (e) {
let curIdx = pageHelper.dataset(e, 'idx');
let curTimesIdx = pageHelper.dataset(e, 'timesidx');
let days = this.data.days;
if (curTimesIdx == -1) {
// 全天
this.setData({
curIdx,
curTimesIdx: -1,
curTimeIsLimit: false,
curTimeLimit: 50,
curTimeLimitModalShow: true
});
} else {
// 时间段
let node = days[curIdx].times[curTimesIdx];
let curTimeIsLimit = node.isLimit;
let curTimeLimit = node.limit;
this.setData({
curIdx,
curTimesIdx,
curTimeIsLimit,
curTimeLimit,
curTimeLimitModalShow: true
});
}
},
_selectTemp: function (e) {
let curIdx = pageHelper.dataset(e, 'idx');
if (this._checkHasJoinCnt(this.data.days[curIdx].times)) {
return pageHelper.showModal('该日已有用户预约/预约待审核,不能选用模板。若确定要选用模板,请先删除有预约的时段');
}
this.setData({
curIdx
});
wx.navigateTo({
url: '../temp/admin_temp_select',
});
},
url: function (e) {
pageHelper.url(e, this);
},
_saveTempModal: function (e) {
let curIdx = pageHelper.dataset(e, 'idx');
let days = this.data.days;
if (days[curIdx].times.length <= 0) return pageHelper.showModal('该日期下没有设置时段,无法保存为模板,请先添加时段');
this.setData({
saveTempModalShow: true,
curIdx
});
},
_copyDaySetToAll: function (e) { // 复制到所有
let curIdx = pageHelper.dataset(e, 'idx');
let days = this.data.days;
let day = days[curIdx].day;
let temps = days[curIdx].times;
let callback = () => {
for (let k = 0; k < days.length; k++) {
if (this._checkHasJoinCnt(days[k].times)) continue; //自己和有记录不复制
let times = [];
for (let j in temps) {
let node = AdminMeetBiz.getNewTimeNode(days[k].day);
node.start = temps[j].start;
node.end = temps[j].end;
node.limit = temps[j].limit;
node.isLimit = temps[j].isLimit;
times.push(node);
}
days[k].times = times;
}
this.setData({
days
});
}
pageHelper.showConfirm('确认将「' + day + '」下的时段设置复制到其他日期下吗? (原有时段将被清除,如已有预约记录则该日的所有时段将不被修改)', callback);
},
bindDaySetTap: async function (e) {
let itemList = ['选用模板配置', '保存为模板', '删除该日期', '复制到所有日期'];
wx.showActionSheet({
itemList,
success: async res => {
let idx = res.tapIndex;
if (idx == 0) { // 选用模板配置
this._selectTemp(e);
}
if (idx == 1) { // 保存为模板
this._saveTempModal(e);
}
if (idx == 2) { // 删除
let curIdx = pageHelper.dataset(e, 'idx');
if (this._checkHasJoinCnt(this.data.days[curIdx].times)) {
return pageHelper.showModal('该日已有用户预约/预约待审核,不能直接删除。若确定要删除,请先删除有预约的时段')
}
let callback = () => {
let days = this.data.days;
days.splice(curIdx, 1);
this.setData({
days
});
this._syncCalData();
}
pageHelper.showConfirm('确认删除该日期吗?', callback);
}
if (idx == 3) { //复制到所有
this._copyDaySetToAll(e);
}
},
fail: function (res) {}
})
},
bindTimeSetTap: async function (e) {
let itemList = ['复制到所有日期', '选用模板配置', '保存为模板'];
wx.showActionSheet({
itemList,
success: async res => {
let idx = res.tapIndex;
if (idx == 0) { // 复制到所有
this._copyDaySetToAll(e);
}
if (idx == 1) { // 选用模板配置
this._selectTemp(e);
}
if (idx == 2) { // 保存为模板
this._saveTempModal(e);
}
},
fail: function (res) {}
})
},
bindDataCalendarClickCmpt: function (e) {
// 数据日历点击
let clickDays = e.detail.days;
if (!clickDays) return;
let days = this.data.days;
let retDays = [];
for (let k = 0; k < clickDays.length; k++) {
let dayExist = false;
for (let j in days) {
if (days[j].day == clickDays[k]) {
// 节点存在
retDays.push(days[j]);
dayExist = true;
break;
};
}
// 节点不存在
if (!dayExist) {
let dayDesc = timeHelper.fmtDateCHN(clickDays[k]) + ' (' + timeHelper.week(clickDays[k]) + ')';
let times = [AdminMeetBiz.getNewTimeNode(clickDays[k])];
let node = {
day: clickDays[k],
dayDesc,
times
};
retDays.push(node);
}
}
this.setData({
days: retDays
});
},
onPageScroll: function (e) {
if (e.scrollTop > 100) {
this.setData({
topShow: true
});
} else {
this.setData({
topShow: false
});
}
},
bindClearReasonTap: function (e) {
this.setData({
formReason: ''
})
},
bindTopTap: function () {
wx.pageScrollTo({
scrollTop: 0
})
},
bindSaveTap: function () {
let parent = pageHelper.getPrevPage(2);
if (!parent) {
pageHelper.showNoneToast('前序页面不存在');
return;
}
let days = this.data.days;
let getDays = [];
if (!projectSetting.MEET_CAN_NULL_TIME) { // 是否允许无时段日期
for (let k = 0; k < days.length; k++) {
if (days[k].times.length > 0) getDays.push(days[k]);
}
} else
getDays = days;
let formDaysSet = this.data.lastHasDays.concat(getDays);
parent.setData({
formDaysSet
});
wx.navigateBack();
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/time/admin_meet_time.json
================================================
{
"usingComponents": {
"cmpt-picker-multi": "/cmpts/public/picker_multi/picker_multi_cmpt",
"cmpt-calendar": "/cmpts/public/calendar/calendar_meet/calendar_meet_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "时间设置"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/time/admin_meet_time.wxml
================================================
时段设置 (共{{days.length+lastHasDays.length}}天可约)
时段设置:请先选择以上日期
{{item.dayDesc}}
尚未添加时段,请设置
{{timesItem.start}}
~
{{timesItem.end}}
{{!timesItem.isLimit?'不限人数':timesItem.limit+'人'}}
添加时段
人数上限
复制/模板
不保存,返回
保存时间设置
{{days[curIdx].day}} {{days[curIdx].times[curTimesIdx].start}}~{{days[curIdx].times[curTimesIdx].end}}
可约人数上限为
{{days[curIdx].day}} 全天 可约人数上限为
是否限制人数
人数上限
人
不限制人数
取消理由 (选填):
清空
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/meet/time/admin_meet_time.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.main-admin {
width: 100%;
box-sizing: border-box;
padding: 10rpx 0;
padding-bottom: 150rpx;
}
.form-box {
border-radius: 0;
}
.form-area {
width: 100%;
box-sizing: border-box;
padding: 30rpx 0rpx;
}
.data-hint {
width: 100%;
box-sizing: border-box;
line-height: 2.5;
background-color: #fff;
text-align: center;
color: #333;
font-size: 36rpx;
}
.data-hint .text-day {
font-size: 30rpx;
}
.select-date {
background-color: #fff;
width: 100%;
z-index: 999;
margin-bottom: 20rpx;
}
.time-group {
display: flex;
justify-content: flex-start;
width: 100%;
flex-direction: column;
align-items: center;
padding: 20rpx 0rpx 30rpx;
}
.time-title {
position: relative;
width: 100%;
font-size: 36rpx;
color: #333;
display: flex;
justify-content: center;
margin-bottom: 30rpx;
}
.time-title .icon-moreandroid {
position: absolute;
right: 0rpx;
color: #888;
width: 100rpx;
text-align: right;
padding-right: 20rpx;
}
.time-line {
width: 100%;
color: #333;
display: flex;
justify-content: center;
padding: 15rpx 0;
position: relative;
}
.time-line.time-line-hint {
font-size: 28rpx;
padding: 5rpx 0;
}
.time-line .box {
line-height: 65rpx;
font-size: 32rpx;
text-align: center;
border: 2rpx solid #ddd;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.time-line .box.close {
border: 0;
color: #888;
min-width: 90rpx;
margin-left: 10rpx;
font-size: 40rpx;
}
.time-line .limit-status {
display: flex;
justify-content: center;
align-items: center;
}
.time-line .x-lock {
position: absolute;
left: 10rpx;
}
.time-line .all {
width: 320rpx;
}
.time-line .clock {
width: 135rpx;
}
.time-line .clock-line {
width: 40rpx;
text-align: center;
color: #888;
}
.time-line .limit {
width: 150rpx;
margin-left: 16rpx;
color: #888;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
}
.time-line .limit .icon-right {
position: absolute;
color: #ccc;
right: 0rpx;
font-size: 24rpx;
}
.time-oprt {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
line-height: 3;
font-size: 32rpx;
margin-top: 10rpx;
color: var(--adminColor);
}
.time-oprt .op {
padding: 0 15rpx;
}
.time-oprt .vline {
height: 35rpx;
width: 1rpx;
border-left: 2rpx dotted #aaa;
}
.text-arrow {
font-size: 24rpx;
position: absolute;
right: 1rpx;
color: #ccc;
}
.modal-form .form-group .input-temp {
height: 100rpx;
text-align: center;
border: 1rpx solid #ccc;
background-color: #fff;
}
.bottom-btn {
width: 100%;
position: fixed;
bottom: 0;
text-align: center;
color: #fff;
line-height: 2.6;
font-size: 36rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/add/admin_mgr_add.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const PublicBiz = require('../../../../../../comm/biz/public_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
Page({
/**
* 页面的初始数据
*/
data: {
formName: '',
formDesc: '',
formPhone: '',
formPassword: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this, true)) return;
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 数据提交
*/
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this, true)) return;
let data = this.data;
// 数据校验
data = validate.check(data, AdminBiz.CHECK_FORM_MGR_ADD, this);
if (!data) return;
try {
let adminId = this.data.id;
data.id = adminId;
await cloudHelper.callCloudSumbit('admin/mgr_insert', data).then(res => {
let callback = async function () {
PublicBiz.removeCacheList('admin-mgr');
wx.navigateBack();
}
pageHelper.showSuccToast('添加成功', 1500, callback);
});
} catch (err) {
console.log(err);
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/add/admin_mgr_add.json
================================================
{
"usingComponents": {},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "添加管理员"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/add/admin_mgr_add.wxml
================================================
登录账号
{{formNameFocus}}
姓名
{{formDescFocus}}
手机
{{formPhoneFocus}}
密码
{{formPasswordFocus}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/add/admin_mgr_add.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/edit/admin_mgr_edit.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this, true)) return;
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
_loadDetail: async function () {
if (!AdminBiz.isAdmin(this, true)) return;
let id = this.data.id;
if (!id) return;
let params = {
id
};
let opt = {
title: 'bar'
};
let mgr = await cloudHelper.callCloudData('admin/mgr_detail', params, opt);
if (!mgr) {
this.setData({
isLoad: null
})
return;
};
this.setData({
isLoad: true,
// 表单数据
formName: mgr.ADMIN_NAME,
formDesc: mgr.ADMIN_DESC,
formPhone: mgr.ADMIN_PHONE,
formPassword: ''
});
},
/**
* 数据提交
*/
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this, true)) return;
let data = this.data;
// 数据校验
data = validate.check(data, AdminBiz.CHECK_FORM_MGR_EDIT, this);
if (!data) return;
try {
let adminId = this.data.id;
data.id = adminId;
await cloudHelper.callCloudSumbit('admin/mgr_edit', data).then(res => {
let callback = () => {
// 更新列表页面数据
let node = {
'ADMIN_NAME': data.name,
'ADMIN_DESC': data.desc,
'ADMIN_PHONE': data.phone,
}
pageHelper.modifyPrevPageListNodeObject(adminId, node);
wx.navigateBack();
}
pageHelper.showSuccToast('修改成功', 1500, callback);
});
} catch (err) {
console.log(err);
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/edit/admin_mgr_edit.json
================================================
{
"usingComponents": {},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "修改管理员"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/edit/admin_mgr_edit.wxml
================================================
登录账号
{{formNameFocus}}
姓名
{{formDescFocus}}
手机
{{formPhoneFocus}}
不修改密码则保持以下为空
新密码
{{formPasswordFocus}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/edit/admin_mgr_edit.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/list/admin_mgr_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isSuperAdmin: false,
dataList: {
list: []
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this, true)) return;
//设置搜索菜单
this.setData(this._getSearchMenu());
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
bindStatusTap: async function (e) {
if (!AdminBiz.isAdmin(this, true)) return;
let id = pageHelper.dataset(e, 'id');
let status = pageHelper.dataset(e, 'status');
if (!id || !status) return;
status = Number(status);
let params = {
id,
status
}
let that = this;
try {
await cloudHelper.callCloudSumbit('admin/mgr_status', params).then(res => {
pageHelper.modifyListNode(id, that.data.dataList.list, 'ADMIN_STATUS', status, '_id');
that.setData({
dataList: that.data.dataList,
});
pageHelper.showSuccToast('设置成功');
});
} catch (e) {
console.log(e);
}
},
bindDelTap: async function (e) {
if (!AdminBiz.isAdmin(this, true)) return;
let id = e.currentTarget.dataset.id;
if (!id) return;
let params = {
id,
}
let callback = async () => {
try {
await cloudHelper.callCloudSumbit('admin/mgr_del', params).then(res => {
pageHelper.delListNode(id, this.data.dataList.list, '_id');
this.data.dataList.total--;
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('删除成功', 2000);
});
} catch (e) {
console.log(e);
}
}
pageHelper.showConfirm('确认删除?删除不可恢复', callback);
},
bindCommListCmpt: function (e) {
if (!AdminBiz.isAdmin(this, true)) return;
pageHelper.commListListener(this, e);
},
url: function (e) {
pageHelper.url(e, this);
},
_getSearchMenu: function () {
let sortItems = [];
let sortMenus = [{
label: '全部',
type: '',
value: ''
}, {
label: '超管',
type: 'type',
value: 1
},
{
label: '普通',
type: 'type',
value: 0
},
{
label: '正常',
type: 'status',
value: 1
},
{
label: '停用',
type: 'type',
value: 0
}
]
return {
sortItems,
sortMenus
}
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/list/admin_mgr_list.json
================================================
{
"usingComponents": {},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "管理员管理"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/list/admin_mgr_list.wxml
================================================
共有{{dataList.total}}条符合条件记录
{{index+1}}
管理员身份
:
超级管理员
普通管理员
登录账号
:
{{item.ADMIN_NAME}}
姓名
:
{{item.ADMIN_DESC||'未填写'}}
手机
:
{{item.ADMIN_PHONE}}
最近登录
:
{{item.ADMIN_LOGIN_CNT}}次 / {{item.ADMIN_LOGIN_TIME}}
编辑
启用
停用
删除
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/list/admin_mgr_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/log/admin_log_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this)) return;
//设置搜索菜单
this.setData(this._getSearchMenu());
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
bindClearTap: async function (e) {
let cb = async () => {
try {
await cloudHelper.callCloudSumbit('admin/log_clear').then(res => {
let cb = () =>{
wx.redirectTo({
url: 'admin_log_list',
})
}
pageHelper.showSuccToast('清空完成', 1500, cb);
})
}
catch (err) {
console.log(err);
}
}
pageHelper.showConfirm('确认清空?清空不可恢复', cb);
},
_getSearchMenu: function () {
let sortItems = [];
let sortMenus = [{
label: '全部',
type: '',
value: ''
}, {
label: '系统',
type: 'type',
value: 0
},
{
label: '用户',
type: 'type',
value: 1
},
{
label: '文章',
type: 'type',
value: 2
},
{
label: '其他',
type: 'type',
value: 99
}
]
return {
sortItems,
sortMenus
}
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/log/admin_log_list.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"disableScroll": true,
"navigationBarTitleText": "后台-操作日志"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/log/admin_log_list.wxml
================================================
共有{{dataList.total}}条符合条件记录
{{index+1}}
操作人
:
{{item.LOG_ADMIN_NAME}} ({{item.LOG_ADMIN_DESC}})
操作时间
:
{{item.LOG_ADD_TIME}}
操作内容
:
{{item.LOG_CONTENT}}
IP地址
:
{{item.LOG_ADD_IP}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/log/admin_log_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
page {
background-color: #f8f8f8;
}
.admin-comm-list .item .info {
padding: 15rpx 20rpx 0rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/pwd/admin_mgr_pwd.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
Page({
/**
* 页面的初始数据
*/
data: {
formOldPassword: '',
formPassword: '',
formPassword2: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this)) return;
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 数据提交
*/
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
let data = this.data;
// 数据校验
data = validate.check(data, AdminBiz.CHECK_FORM_MGR_PWD, this);
if (!data) return;
if (data.password != data.password2) {
return pageHelper.showModal('两次输入的新密码不一致');
}
try {
await cloudHelper.callCloudSumbit('admin/mgr_pwd', data).then(res => {
let callback = () => {
wx.navigateBack();
}
pageHelper.showSuccToast('修改成功', 1500, callback);
});
} catch (err) {
console.log(err);
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/pwd/admin_mgr_pwd.json
================================================
{
"usingComponents": {},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "管理员密码修改"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/pwd/admin_mgr_pwd.wxml
================================================
旧密码
{{formOldPasswordFocus}}
新密码
{{formPasswordFocus}}
新密码再次填写
{{formPassword2Focus}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/mgr/pwd/admin_mgr_pwd.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/add/admin_news_add.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const PublicBiz = require('../../../../../../comm/biz/public_biz.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
const AdminNewsBiz = require('../../../../biz/admin_news_biz.js');
const projectSetting = require('../../../../public/project_setting.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
wx.setNavigationBarTitle({
title: projectSetting.NEWS_NAME + '-添加',
});
this.setData(AdminNewsBiz.initFormData()); // 初始化表单数据
this.setData({
isLoad: true
});
this._setContentDesc();
},
_setContentDesc: function () {
AdminBiz.setContentDesc(this);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
model: function (e) {
pageHelper.model(this, e);
},
/**
* 数据提交
*/
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
let data = this.data;
if (this.data.formContent.length == 0) {
return pageHelper.showModal('详细内容不能为空');
}
data = validate.check(data, AdminNewsBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
data.cateName = AdminNewsBiz.getCateName(data.cateId);
try {
if (this.data.imgList.length == 0) {
return pageHelper.showModal('请上传封面图');
}
// 提取简介
data.desc = PublicBiz.getRichEditorDesc(data.desc, this.data.formContent);
// 先创建,再上传
let result = await cloudHelper.callCloudSumbit('admin/news_insert', data);
let newsId = result.data.id;
// 封面图片 提交处理
wx.showLoading({
title: '提交中...',
mask: true
});
await cloudHelper.transCoverTempPics(this.data.imgList, 'news/', newsId, 'admin/news_update_pic');
// 富文本
let formContent = this.data.formContent;
if (formContent && formContent.length > 0) {
wx.showLoading({
title: '提交中...',
mask: true
});
let content = await cloudHelper.transRichEditorTempPics(formContent, 'news/', newsId, 'admin/news_update_content');
this.setData({
formContent: content
});
}
await cloudHelper.transFormsTempPics(forms, 'news/', newsId, 'admin/news_update_forms');
let callback = async function () {
PublicBiz.removeCacheList('admin-news-list');
PublicBiz.removeCacheList('news-list');
wx.navigateBack();
}
pageHelper.showSuccToast('添加成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
bindImgUploadCmpt: function (e) {
this.setData({
imgList: e.detail
});
},
url: function (e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/add/admin_news_add.json
================================================
{
"usingComponents": {
"cmpt-img-upload": "/cmpts/public/img/img_upload_cmpt",
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/add/admin_news_add.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/add/admin_news_add.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/admin_news_form_tpl.wxml
================================================
标题
{{formTitleFocus}}
分类
{{formCateIdFocus}}
排序号(小的先显示)
{{formOrderFocus}}
简介
{{formDesc.length}}/100
{{formDescFocus}}
详细内容(必填)
{{contentDesc}}
{{formContentFocus}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/edit/admin_news_edit.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
const AdminNewsBiz = require('../../../../biz/admin_news_biz.js');
const PublicBiz = require('../../../../../../comm/biz/public_biz.js');
const projectSetting = require('../../../../public/project_setting.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (!pageHelper.getOptions(this, options)) return;
wx.setNavigationBarTitle({
title: projectSetting.NEWS_NAME + '-修改',
});
this._loadDetail();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
model: function (e) {
pageHelper.model(this, e);
},
_loadDetail: async function () {
if (!AdminBiz.isAdmin(this)) return;
let id = this.data.id;
if (!id) return;
if (!this.data.isLoad) this.setData(AdminNewsBiz.initFormData(id)); // 初始化表单数据
let params = {
id
};
let opt = {
title: 'bar'
};
let news = await cloudHelper.callCloudData('admin/news_detail', params, opt);
if (!news) {
this.setData({
isLoad: null
})
return;
};
this.setData({
isLoad: true,
imgList: news.NEWS_PIC,
// 表单数据
formCateId: news.NEWS_CATE_ID,
formOrder: news.NEWS_ORDER,
formTitle: news.NEWS_TITLE,
formContent: news.NEWS_CONTENT,
formDesc: news.NEWS_DESC,
formForms: news.NEWS_FORMS,
}, () => {
this._setContentDesc();
});
},
_setContentDesc: function () {
AdminBiz.setContentDesc(this);
},
/**
* 数据提交
*/
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
// 数据校验
let data = this.data;
if (this.data.formContent.length == 0) {
return pageHelper.showModal('详细内容不能为空');
}
data = validate.check(data, AdminNewsBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
data.cateName = AdminNewsBiz.getCateName(data.cateId);
try {
let newsId = this.data.id;
data.id = newsId;
if (this.data.imgList.length == 0) {
return pageHelper.showModal('请上传封面图');
}
// 提取简介
data.desc = PublicBiz.getRichEditorDesc(data.desc, this.data.formContent);
// 先修改,再上传
await cloudHelper.callCloudSumbit('admin/news_edit', data);
// 封面图片 提交处理
wx.showLoading({
title: '提交中...',
mask: true
});
await cloudHelper.transCoverTempPics(this.data.imgList, 'news/', newsId, 'admin/news_update_pic');
// 富文本图片
let formContent = this.data.formContent;
wx.showLoading({
title: '提交中...',
mask: true
});
let content = await cloudHelper.transRichEditorTempPics(formContent, 'news/', newsId, 'admin/news_update_content');
this.setData({
formContent: content
});
await cloudHelper.transFormsTempPics(forms, 'news/', newsId, 'admin/news_update_forms');
let callback = async () => {
// 更新列表页面数据
let node = {
'NEWS_TITLE': data.title,
'NEWS_CATE_NAME': data.cateName,
'NEWS_ORDER': data.order,
}
pageHelper.modifyPrevPageListNodeObject(newsId, node);
wx.navigateBack();
}
pageHelper.showSuccToast('修改成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
bindImgUploadCmpt: function (e) {
this.setData({
imgList: e.detail
});
},
url: function (e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/edit/admin_news_edit.json
================================================
{
"usingComponents": {
"cmpt-img-upload": "/cmpts/public/img/img_upload_cmpt",
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/edit/admin_news_edit.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/edit/admin_news_edit.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/list/admin_news_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const NewsBiz = require('../../../../biz/news_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const projectSetting = require('../../../../public/project_setting.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
wx.setNavigationBarTitle({
title: projectSetting.NEWS_NAME + '-管理',
});
this.setData({
NEWS_NAME: projectSetting.NEWS_NAME
});
//设置搜索菜单
this._getSearchMenu();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
_setSort: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = e.currentTarget.dataset.id;
let sort = e.currentTarget.dataset.sort;
if (!id) return;
let params = {
id,
sort
}
try {
await cloudHelper.callCloudSumbit('admin/news_sort', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'NEWS_ORDER', sort);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (e) {
console.log(e);
}
},
_setVouch: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let vouch = pageHelper.dataset(e, 'vouch');
if (!id) return;
let params = {
id,
vouch
}
try {
await cloudHelper.callCloudSumbit('admin/news_vouch', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'NEWS_VOUCH', vouch);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_del: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let params = {
id
}
let callback = async () => {
try {
let opts = {
title: '删除中'
}
await cloudHelper.callCloudSumbit('admin/news_del', params, opts).then(res => {
pageHelper.delListNode(id, this.data.dataList.list, '_id');
this.data.dataList.total--;
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('删除成功');
});
} catch (e) {
console.log(e);
}
}
pageHelper.showConfirm('确认删除?删除不可恢复', callback);
},
bindMoreTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let idx = pageHelper.dataset(e, 'idx');
let order = this.data.dataList.list[idx].NEWS_ORDER;
let orderDesc = (order == 0) ? '取消置顶' : '置顶';
let vouch = this.data.dataList.list[idx].NEWS_VOUCH;
let vouchDesc = (vouch == 0) ? '推荐到首页' : '取消首页推荐';
let itemList = ['预览', orderDesc, vouchDesc, '生成专属二维码'];
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //预览
let id = pageHelper.dataset(e, 'id');
wx.navigateTo({
url: '../../../news/detail/news_detail?id=' + id,
});
break;
}
case 1: { //置顶
let sort = (order == 0) ? 9999 : 0;
e.currentTarget.dataset['sort'] = sort;
await this._setSort(e);
break;
}
case 2: { //上首页
vouch = (vouch == 0) ? 1 : 0;
e.currentTarget.dataset['vouch'] = vouch;
await this._setVouch(e);
break;
}
case 3: { //二维码
let title = encodeURIComponent(pageHelper.dataset(e, 'title'));
let qr = encodeURIComponent(pageHelper.dataset(e, 'qr'));
wx.navigateTo({
url: `../../setup/qr/admin_setup_qr?title=${title}&qr=${qr}`,
})
break;
}
}
},
fail: function (res) { }
})
},
bindStatusMoreTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let itemList = ['启用', '停用 (不可见)', '删除'];
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //启用
e.currentTarget.dataset['status'] = 1;
await this._setStatus(e);
break;
}
case 1: { //停止
e.currentTarget.dataset['status'] = 0;
await this._setStatus(e);
break;
}
case 2: { //删除
await this._del(e);
break;
}
}
},
fail: function (res) { }
})
},
_setStatus: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let status = Number(pageHelper.dataset(e, 'status'));
let params = {
id,
status
}
try {
await cloudHelper.callCloudSumbit('admin/news_status', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'NEWS_STATUS', status, '_id');
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (e) {
console.log(e);
}
},
_getSearchMenu: function () {
let cateIdOptions = NewsBiz.getCateList();
let sortItem1 = [{ label: '分类', type: '', value: 0 }];
sortItem1 = sortItem1.concat(NewsBiz.getCateList());
let sortItems = [sortItem1];
let sortMenus = [
{ label: '全部', type: '', value: '' },
{ label: '正常', type: 'status', value: 1 },
{ label: '停用', type: 'status', value: 0 },
{ label: '最新', type: 'sort', value: 'new' },
{ label: '首页推荐', type: 'vouch', value: 'vouch' },
{ label: '置顶', type: 'top', value: 'top' },
];
this.setData({
search: '',
cateIdOptions,
sortItems,
sortMenus,
isLoad: true
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/list/admin_news_list.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"disableScroll": true
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/list/admin_news_list.wxml
================================================
共有{{dataList.total}}条符合条件记录
{{index+1}}
分类
:
『{{item.NEWS_CATE_NAME}}』
排序号
:
{{item.NEWS_ORDER}} (小的先显示)
创建
:
{{item.NEWS_ADD_TIME}}
编辑
状态管理
更多操作
预览
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/news/list/admin_news_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
@import '../../../../../../style/project/admin_list_style.wxss';
page {
background-color: #f8f8f8;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/add/admin_product_add.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const PublicBiz = require('../../../../../../comm/biz/public_biz.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const validate = require('../../../../../../helper/validate.js');
const AdminProductBiz = require('../../../../biz/admin_product_biz.js');
const ProductBiz = require('../../../../biz/product_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
this.setData(AdminProductBiz.initFormData());
this.setData({
isLoad: true
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
url: function (e) {
pageHelper.url(e, this);
},
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
let data = this.data;
data = validate.check(data, AdminProductBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
data.cateName = ProductBiz.getCateName(data.cateId);
try {
// 创建
let result = await cloudHelper.callCloudSumbit('admin/product_insert', data);
let productId = result.data.id;
// 图片
await cloudHelper.transFormsTempPics(forms, 'product/', productId, 'admin/product_update_forms');
let callback = async function () {
PublicBiz.removeCacheList('admin-product-list');
PublicBiz.removeCacheList('product-list');
wx.navigateBack();
}
pageHelper.showSuccToast('添加成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/add/admin_product_add.json
================================================
{
"usingComponents": {
"cmpt-img-upload": "/cmpts/public/img/img_upload_cmpt",
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "后台-景点添加"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/add/admin_product_add.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/add/admin_product_add.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/admin_product_form_tpl.wxml
================================================
标题
{{formTitleFocus}}
分类
{{formCateIdFocus}}
排序号(小的先显示)
{{formOrderFocus}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/edit/admin_product_edit.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const dataHelper = require('../../../../../../helper/data_helper.js');
const validate = require('../../../../../../helper/validate.js');
const AdminProductBiz = require('../../../../biz/admin_product_biz.js');
const ProductBiz = require('../../../../biz/product_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
this.selectComponent("#cmpt-form").reload();
wx.stopPullDownRefresh();
},
model: function (e) {
pageHelper.model(this, e);
},
_loadDetail: async function () {
if (!AdminBiz.isAdmin(this)) return;
let id = this.data.id;
if (!id) return;
if (!this.data.isLoad) this.setData(AdminProductBiz.initFormData(id)); // 初始化表单数据
let params = {
id
};
let opt = {
title: 'bar'
};
let product = await cloudHelper.callCloudData('admin/product_detail', params, opt);
if (!product) {
this.setData({
isLoad: null
})
return;
};
this.setData({
isLoad: true,
formTitle: product.PRODUCT_TITLE,
formCateId: product.PRODUCT_CATE_ID,
formOrder: product.PRODUCT_ORDER,
formForms: product.PRODUCT_FORMS,
});
},
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
// 数据校验
let data = this.data;
data = validate.check(data, AdminProductBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
data.cateName = ProductBiz.getCateName(data.cateId);
try {
let productId = this.data.id;
data.id = productId;
// 先修改,再上传
await cloudHelper.callCloudSumbit('admin/product_edit', data);
await cloudHelper.transFormsTempPics(forms, 'product/', productId, 'admin/product_update_forms');
let callback = () => {
// 更新列表页面数据
let node = {
'PRODUCT_TITLE': data.title,
'PRODUCT_CATE_NAME': data.cateName,
'PRODUCT_ORDER': data.order,
}
pageHelper.modifyPrevPageListNodeObject(productId, node);
wx.navigateBack();
}
pageHelper.showSuccToast('修改成功', 2000, callback);
} catch (err) {
console.log(err);
}
},
url: function (e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/edit/admin_product_edit.json
================================================
{
"usingComponents": {
"cmpt-img-upload": "/cmpts/public/img/img_upload_cmpt",
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "后台-景点修改"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/edit/admin_product_edit.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/edit/admin_product_edit.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/list/admin_product_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const ProductBiz = require('../../../../biz/product_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
//设置搜索菜单
this._getSearchMenu();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
bindStatusMoreTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let itemList = ['启用', '停用 (不显示)', '删除'];
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //启用
e.currentTarget.dataset['status'] = 1;
await this._setStatus(e);
break;
}
case 1: { //停止
e.currentTarget.dataset['status'] = 0;
await this._setStatus(e);
break;
}
case 2: { //删除
await this._del(e);
break;
}
}
},
fail: function (res) { }
})
},
bindMoreTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let idx = pageHelper.dataset(e, 'idx');
let order = this.data.dataList.list[idx].PRODUCT_ORDER;
let orderDesc = (order == 0) ? '取消置顶' : '置顶';
let vouch = this.data.dataList.list[idx].PRODUCT_VOUCH;
let vouchDesc = (vouch == 0) ? '推荐到首页' : '取消首页推荐';
let itemList = ['预览', orderDesc, vouchDesc, '生成专属二维码'];
wx.showActionSheet({
itemList,
success: async res => {
switch (res.tapIndex) {
case 0: { //预览
let id = pageHelper.dataset(e, 'id');
wx.navigateTo({
url: '../../../product/detail/product_detail?id=' + id,
});
break;
}
case 1: { //置顶
let sort = (order == 0) ? 9999 : 0;
e.currentTarget.dataset['sort'] = sort;
await this._setSort(e);
break;
}
case 2: { //上首页
vouch = (vouch == 0) ? 1 : 0;
e.currentTarget.dataset['vouch'] = vouch;
await this._setVouch(e);
break;
}
case 3: { //二维码
let title = encodeURIComponent(pageHelper.dataset(e, 'title'));
let qr = encodeURIComponent(pageHelper.dataset(e, 'qr'));
wx.navigateTo({
url: `../../setup/qr/admin_setup_qr?title=${title}&qr=${qr}`,
})
break;
}
}
},
fail: function (res) { }
})
},
_setSort: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let sort = pageHelper.dataset(e, 'sort');
if (!id) return;
let params = {
id,
sort
}
try {
await cloudHelper.callCloudSumbit('admin/product_sort', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'PRODUCT_ORDER', sort);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_setVouch: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let vouch = pageHelper.dataset(e, 'vouch');
if (!id) return;
let params = {
id,
vouch
}
try {
await cloudHelper.callCloudSumbit('admin/product_vouch', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'PRODUCT_VOUCH', vouch);
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_del: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let params = {
id
}
let callback = async () => {
try {
let opts = {
title: '删除中'
}
await cloudHelper.callCloudSumbit('admin/product_del', params, opts).then(res => {
pageHelper.delListNode(id, this.data.dataList.list, '_id');
this.data.dataList.total--;
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('删除成功');
});
} catch (err) {
console.log(err);
}
}
pageHelper.showConfirm('确认删除?删除不可恢复', callback);
},
_setStatus: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let status = Number(pageHelper.dataset(e, 'status'));
let params = {
id,
status
}
try {
await cloudHelper.callCloudSumbit('admin/product_status', params).then(res => {
pageHelper.modifyListNode(id, this.data.dataList.list, 'PRODUCT_STATUS', status, '_id');
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('设置成功');
});
} catch (err) {
console.log(err);
}
},
_getSearchMenu: function () {
let cateIdOptions = ProductBiz.getCateList();
let sortItem1 = [{ label: '分类', type: '', value: 0 }];
sortItem1 = sortItem1.concat(cateIdOptions);
let sortItem2 = [
{ label: '排序', type: '', value: 0 },
{ label: '推荐指数从高到底', type: 'sort', value: 'PRODUCT_OBJ.star|desc' },
{ label: '推荐指数从低到高', type: 'sort', value: 'PRODUCT_OBJ.star|asc' },
];
let sortItems = [];
if (sortItem1.length > 2) sortItems.push(sortItem1);
sortItems.push(sortItem2);
let sortMenus = [
{ label: '全部', type: '', value: '' },
{ label: '正常', type: 'status', value: 1 },
{ label: '停用', type: 'status', value: 0 },
{ label: '最新', type: 'sort', value: 'new' },
{ label: '首页推荐', type: 'vouch', value: 'vouch' },
{ label: '置顶', type: 'top', value: 'top' },
]
this.setData({
cateIdOptions,
sortItems,
sortMenus
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/list/admin_product_list.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"disableScroll": true,
"navigationBarTitleText": "后台-景点管理"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/list/admin_product_list.wxml
================================================
共有{{dataList.total}}条符合条件记录
{{index+1}}
分类
:
『{{item.PRODUCT_CATE_NAME}}』
排序号
:
{{item.PRODUCT_ORDER}} (小的先显示)
推荐指数
:
{{item.PRODUCT_OBJ.star}}颗星
创建
:
{{item.PRODUCT_ADD_TIME}}
编辑
状态管理..
更多...
预览
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/product/list/admin_product_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
@import '../../../../../../style/project/admin_list_style.wxss';
page {
background-color: #f8f8f8;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about/admin_setup_about.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const projectSetting = require('../../../../public/project_setting.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
key: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (options && options.key) {
let key = options.key;
for (let k = 0; k < projectSetting.SETUP_CONTENT_ITEMS.length; k++) {
let item = projectSetting.SETUP_CONTENT_ITEMS[k];
if (item.key == key) {
this._loadDetail(item);
wx.setNavigationBarTitle({
title: '编辑' + item.title,
});
this.setData({ key: item.key });
break;
}
}
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
_loadDetail: async function (item) {
if (!AdminBiz.isAdmin(this)) return;
let opts = {
'title': 'bar'
};
let params = {
key: item.key
}
try {
await cloudHelper.callCloudSumbit('home/setup_get', params, opts).then(res => {
let formContent = [{ type: 'text', val: item.title }];
let content = res.data;
if (content && Array.isArray(content)) {
formContent = content;
}
this.setData({
isLoad: true,
// 表单数据
formContent
});
});
}
catch (err) {
console.log(err);
}
},
/**
* 数据提交
*/
bindFormSubmit: async function () {
if (!AdminBiz.isAdmin(this)) return;
let formContent = this.selectComponent("#contentEditor").getNodeList();
await cloudHelper.transRichEditorTempPics(formContent, 'setup/', this.data.key, 'admin/setup_set_content');
let callback = () => {
wx.navigateBack();
}
pageHelper.showSuccToast('修改成功', 1500, callback);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about/admin_setup_about.json
================================================
{
"usingComponents": {
"cmpt-editor": "/cmpts/public/editor/editor_cmpt"
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "编辑"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about/admin_setup_about.wxml
================================================
保存修改
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about/admin_setup_about.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.main-admin {
width: 100%;
box-sizing: border-box;
padding: 30rpx 20rpx;
padding-bottom: 200rpx;
}
.form-group {
padding: 1rpx 1rpx;
overflow: hidden;
}
.btn-bottom-admin>view {
width: 100%;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about_list/admin_setup_about_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const projectSetting = require('../../../../public/project_setting.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
if (!AdminBiz.isAdmin(this)) return;
this.setData({
list: projectSetting.SETUP_CONTENT_ITEMS
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
url: function (e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about_list/admin_setup_about_list.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "单页文章"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about_list/admin_setup_about_list.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/about_list/admin_setup_about_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
page {
background-color: #f8f8f8;
}
.main-admin {
padding: 40rpx 0;
}
.admin-comm-list .item {
margin-bottom: 40rpx;
}
.admin-comm-list .item .header .left {
font-size: 32rpx !important;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/qr/admin_setup_qr.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
qrUrl: '',
title: '',
path: '',
sc: '',
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (options && options.qr && options.title) {
this.setData({
qr: decodeURIComponent(options.qr),
title: decodeURIComponent(options.title),
}, () => {
this._loadDetail();
});
}
else
this._loadDetail();
},
_loadDetail: async function () {
if (this.data.qr) {
this.setData({
qrUrl: this.data.qr,
isLoad: true
})
return;
}
let path = pageHelper.fmtURLByPID('/pages/default/index/default_index');
let params = {
path
};
let opt = {
title: 'bar'
};
try {
await cloudHelper.callCloudSumbit('admin/setup_qr', params, opt).then(res => {
this.setData({
qrUrl: res.data,
isLoad: true
});
});
} catch (err) {
console.error(err);
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
url: function (e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/qr/admin_setup_qr.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "小程序码"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/qr/admin_setup_qr.wxml
================================================
放在推广的地方展示
长按图片保存小程序码
《{{title}}》小程序码
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/setup/qr/admin_setup_qr.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.load.loading::after {
content: "小程序码生成中...";
}
.form-box .checkin {
width: 100%;
padding: 40rpx 40rpx 80rpx;
padding-bottom: 150rpx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.form-box .checkin .notice {
width: 100%;
font-size: 36rpx;
text-align: center;
color: #000;
margin-bottom: 30rpx;
}
.form-box .checkin image {
width: 500rpx;
height: 500rpx;
margin: 30rpx 0rpx;
}
.form-box .checkin .oprt {
margin-top: 0rpx;
text-align: center;
font-size: 30rpx;
color: var(--adminColor);
padding: 20rpx 10rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/detail/admin_user_detail.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
if (!AdminBiz.isAdmin(this)) return;
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
async onPullDownRefresh() {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
},
_loadDetail: async function () {
if (!AdminBiz.isAdmin(this)) return;
let id = this.data.id;
if (!id) return;
let params = {
id
}
let opts = {
hint: false
}
let user = await cloudHelper.callCloudData('admin/user_detail', params, opts);
if (!user) {
this.setData({
isLoad: null,
})
return;
};
this.setData({
isLoad: true,
user
})
},
url(e) {
pageHelper.url(e, this);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/detail/admin_user_detail.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "用户详情"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/detail/admin_user_detail.wxml
================================================
用户昵称
:
{{user.USER_NAME}}
手机号码
:
{{user.USER_MOBILE}}
注册时间
:
{{user.USER_ADD_TIME}}
{{item.title}}
:
{{item.val}}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/detail/admin_user_detail.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.main-admin {
padding: 30rpx 0rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/export/admin_user_export.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const fileHelper = require('../../../../../../helper/file_helper.js');
const projectSetting = require('../../../../public/project_setting.js');
Page({
/**
* 页面的初始数据
*/
data: {
url: '',
time: '',
condition: '',
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
if (!AdminBiz.isAdmin(this)) return;
if (options && options.condition) {
this.setData({
condition: options.condition
})
}
this._loadDetail(1);
},
_loadDetail: async function (isDel) {
if (!AdminBiz.isAdmin(this)) return;
let params = {
isDel
}
let options = {
title: 'bar'
}
let data = await cloudHelper.callCloudData('admin/user_data_get', params, options);
if (!data) return;
this.setData({
isLoad: true,
url: data.url,
time: data.time
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail(1);
wx.stopPullDownRefresh();
},
bindOpenTap: function (e) {
fileHelper.openDoc('客户数据', this.data.url);
},
url: async function (e) {
pageHelper.url(e, this);
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
bindExportTap: async function (e) {
try {
let options = {
title: '数据生成中'
}
let params = {
condition: this.data.condition,
fields: projectSetting.USER_FIELDS
}
await cloudHelper.callCloudData('admin/user_data_export', params, options).then(res => {
this._loadDetail(0);
pageHelper.showModal('数据文件生成成功(' + res.total + '条记录), 请点击「直接打开」按钮或者复制文件地址下载');
});
} catch (err) {
console.log(err);
pageHelper.showNoneToast('导出失败,请重试');
}
},
bindDelTap: async function (e) {
try {
let options = {
title: '数据删除中'
}
await cloudHelper.callCloudData('admin/user_data_del', {}, options).then(res => {
this.setData({
url: '',
time: ''
});
pageHelper.showSuccToast('删除成功');
});
} catch (err) {
console.log(err);
pageHelper.showNoneToast('删除失败,请重试');
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/export/admin_user_export.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"navigationBarTitleText": "用户资料导出"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/export/admin_user_export.wxml
================================================
※ 数据说明: 针对本次查询结果导出全部数据
数据下载链接({{time}} 生成)
※ 链接使用说明
1. 复制以上链接地址,建议在电脑浏览器中打开链接下载数据文件
2. 为保障信息安全,请勿外传数据链接
3. 为了防止隐私数据泄露,请在下载后及时点击下方按钮删除
为了防止隐私数据泄露,请在下载上述文件后及时点击按钮删除
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/export/admin_user_export.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
.form-box .title-desc {
padding-bottom: 10rpx;
border: 0;
font-size: 29rpx;
color: #888;
}
.btn-admin{
margin-bottom: 20rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/list/admin_user_list.js
================================================
const AdminBiz = require('../../../../../../comm/biz/admin_biz.js');
const pageHelper = require('../../../../../../helper/page_helper.js');
const cacheHelper = require('../../../../../../helper/cache_helper.js');
const cloudHelper = require('../../../../../../helper/cloud_helper.js');
const projectSetting = require('../../../../public/project_setting.js');
const CACHE_USER_CHECK_REASON = 'CACHE_USER_CHECK_REASON';
Page({
/**
* 页面的初始数据
*/
data: {
userRegCheck: projectSetting.USER_REG_CHECK,
checkModalShow: false,
formReason: '',
curIdx: -1,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (!AdminBiz.isAdmin(this)) return;
//设置搜索菜单
await this._getSearchMenu();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
bindDelTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let id = pageHelper.dataset(e, 'id');
let params = {
id
}
let callback = async () => {
try {
let opts = {
title: '删除中'
}
await cloudHelper.callCloudSumbit('admin/user_del', params, opts).then(res => {
pageHelper.delListNode(id, this.data.dataList.list, 'USER_MINI_OPENID');
this.data.dataList.total--;
this.setData({
dataList: this.data.dataList
});
pageHelper.showSuccToast('删除成功');
});
} catch (e) {
console.log(e);
}
}
pageHelper.showConfirm('确认删除?删除不可恢复', callback);
},
bindClearReasonTap: function (e) {
this.setData({
formReason: ''
})
},
bindCheckTap: function (e) {
let curIdx = pageHelper.dataset(e, 'idx');
this.setData({
formReason: cacheHelper.get(CACHE_USER_CHECK_REASON) || '',
curIdx,
checkModalShow: true,
});
},
bindCheckCmpt: async function () {
let e = {
currentTarget: {
dataset: {
status: 8,
idx: this.data.curIdx
}
}
}
cacheHelper.set(CACHE_USER_CHECK_REASON, this.data.formReason, 86400 * 365);
await this.bindStatusTap(e);
},
bindStatusTap: async function (e) {
if (!AdminBiz.isAdmin(this)) return;
let status = pageHelper.dataset(e, 'status');
let idx = Number(pageHelper.dataset(e, 'idx'));
let dataList = this.data.dataList;
let id = dataList.list[idx].USER_MINI_OPENID;
let params = {
id,
status,
reason: this.data.formReason
}
let cb = async () => {
try {
await cloudHelper.callCloudSumbit('admin/user_status', params).then(res => {
let sortIndex = this.selectComponent('#cmpt-comm-list').getSortIndex();
if (sortIndex != -1 && sortIndex != 5 && !this.data.search) { // 全部或者检索的结果
dataList.list.splice(idx, 1);
dataList.total--;
this.setData({
dataList: this.data.dataList
});
} else {
let data1Name = 'dataList.list[' + idx + '].USER_CHECK_REASON';
let data2Name = 'dataList.list[' + idx + '].USER_STATUS';
this.setData({
[data1Name]: this.data.formReason,
[data2Name]: status
});
}
this.setData({
checkModalShow: false,
formReason: '',
curIdx: -1,
});
pageHelper.showSuccToast('操作成功');
});
} catch (e) {
console.log(e);
}
}
if (status == 8) {
pageHelper.showConfirm('该用户审核不通过,用户修改资料后可重新提交审核', cb)
}
else
pageHelper.showConfirm('确认执行此操作?', cb);
},
_getSearchMenu: async function () {
let sortItems1 = [
{ label: '全部', type: '', value: '' },
{ label: '注册时间从早到晚', type: 'sort', value: 'newasc' },
{ label: '注册时间从晚到早', type: 'sort', value: 'newdesc' },
];
let sortMenus = [
{ label: '全部', type: '', value: '' },
{ label: '正常', type: 'status', value: 1 },
{ label: '禁用', type: 'status', value: 9 }
]
if (projectSetting.USER_REG_CHECK) {
sortMenus = sortMenus.concat([
{ label: '待审核', type: 'status', value: 0 },
{ label: '审核未过', type: 'status', value: 8 }
]);
}
this.setData({
sortItems: [sortItems1],
sortMenus
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/list/admin_user_list.json
================================================
{
"usingComponents": {
},
"navigationBarBackgroundColor": "#2499f2",
"navigationBarTextStyle": "white",
"disableScroll": true,
"navigationBarTitleText": "用户管理"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/list/admin_user_list.wxml
================================================
共有{{dataList.total}}条符合条件记录
{{index+1}}
审核理由
:
{{item.USER_CHECK_REASON||'未填写'}}
手机
:
{{item.USER_MOBILE||'未填写'}}
注册
:
{{item.USER_ADD_TIME}}
详情
:
查看更多用户资料...
审核通过
恢复正常
审核不过
禁用
删除
审核不过理由 (选填):
清空
================================================
FILE: miniprogram/projects/TRIP1/pages/admin/user/list/admin_user_list.wxss
================================================
@import '../../../../../../style/public/admin.wxss';
page {
background-color: #f8f8f8;
}
.admin-comm-list .item .info .oprt {
padding: 0rpx 0rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/album/detail/album_detail.js
================================================
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
_loadDetail: async function () {
let id = this.data.id;
if (!id) return;
let params = {
id,
};
let opt = {
title: 'bar'
};
let album = await cloudHelper.callCloudData('album/view', params, opt);
if (!album) {
this.setData({
isLoad: null
})
return;
}
this.setData({
isLoad: true,
album,
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () { },
url: function (e) {
pageHelper.url(e, this);
},
onPageScroll: function (e) {
// 回页首按钮
pageHelper.showTopBtn(e, this);
},
onShareAppMessage: function (res) {
return {
title: this.data.album.ALBUM_TITLE,
imageUrl: this.data.album.ALBUM_OBJ.cover[0]
}
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/album/detail/album_detail.json
================================================
{
"usingComponents": {
"cmpt-detail": "/cmpts/biz/detail/detail_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "攻略详情"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/album/detail/album_detail.wxml
================================================
{{album.ALBUM_TITLE}}
{{album.ALBUM_CATE_NAME}} {{album.ALBUM_ADD_TIME}}
{{item.val}}
================================================
FILE: miniprogram/projects/TRIP1/pages/album/detail/album_detail.wxss
================================================
@import "../../../../../style/public/detail.wxss";
@import "../../../style/skin.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/album/index/album_index.js
================================================
const ProjectBiz = require('../../../biz/project_biz.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const AlbumBiz = require('../../../biz/album_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
this._getSearchMenu();
if (options && options.id) {
this.setData({
_params: {
sortType: 'cateId',
sortVal: options.id,
}
});
} else {
this.setData({
_params: {
sortType: 'cateId',
sortVal: '',
}
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
onShareAppMessage: function () {
},
_getSearchMenu: function () {
AlbumBiz.setCateTitle();
let sortItem1 = [{
label: '全部',
type: 'cateId',
value: ''
}];
sortItem1 = sortItem1.concat(AlbumBiz.getCateList());
let sortItems = [];
let sortMenus = sortItem1;
this.setData({
sortItems,
sortMenus
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/album/index/album_index.json
================================================
{
"usingComponents": {
},
"disableScroll": true,
"navigationBarTitleText": "攻略"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/album/index/album_index.wxml
================================================
{{item.ALBUM_VIEW_CNT}}
{{item.ALBUM_TITLE}}
{{item.ALBUM_OBJ.desc}}
================================================
FILE: miniprogram/projects/TRIP1/pages/album/index/album_index.wxss
================================================
@import "../../../style/skin.wxss";
page {
background-color: #fff;
}
.album-list {
margin-top: 10rpx;
background-color: #fff;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
padding: 0 20rpx;
}
.album-list .item {
width: 50%;
padding: 10rpx 15rpx 20rpx;
display: flex;
flex-direction: column;
}
.album-list .item .item-inner {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 10rpx;
}
.album-list .item .item-inner image {
width: 100%;
height: 420rpx;
border-top-left-radius: 10rpx;
border-top-right-radius: 10rpx;
border: 1rpx solid #ddd;
position: relative;
}
.album-list .item .item-inner .attention {
padding: 10rpx 20rpx;
background-color: rgba(0, 0, 0, .4);
position: absolute;
right: 10rpx;
bottom: 10rpx;
color: #fff;
border-radius: 40rpx;
}
.album-list .item .item-inner .detail {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
padding: 0 10rpx 20rpx;
}
.album-list .item .item-inner .detail .title {
width: 100%;
text-align: left;
color: #000;
font-size: 32rpx;
font-weight: bold;
margin-top: 20rpx;
}
.album-list .item .item-inner .detail .title .ticon {
font-size: 30rpx;
font-weight: bold;
margin-right: 0rpx;
}
.album-list .item .item-inner .detail .desc {
width: 100%;
text-align: left;
color: #777;
font-size: 24rpx;
margin-top: 20rpx;
height:60rpx;
}
.shadow {
box-shadow: 6rpx 9rpx 8rpx #eee;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/default/index/default_index.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
album: [
'/projects/TRIP1/images/home/b1.jpg',
'/projects/TRIP1/images/home/b3.jpg',
'/projects/TRIP1/images/home/b4.jpg',
'/projects/TRIP1/images/home/b5.jpg',
]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
},
_loadList: async function () {
let opts = {
title: 'bar'
}
await cloudHelper.callCloudSumbit('home/list', {}, opts).then(res => {
let dataList = res.data;
let hot1List = [];
let hot2List = [];
for (let k = 0; k < dataList.length; k++) {
let item = dataList[k];
if (item.type == 'product') hot1List.push(item);
else if (item.type == 'album')
hot2List.push(item);
}
this.setData({
hot1List,
hot2List
})
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () {
this._loadList();
},
onPullDownRefresh: async function () {
await this._loadList();
wx.stopPullDownRefresh();
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/default/index/default_index.json
================================================
{
"usingComponents": {
"cmpt-swiper": "/cmpts/public/swiper/swiper_cmpt"
},
"enablePullDownRefresh": true,
"navigationStyle": "custom",
"navigationBarTitleText": "首页"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/default/index/default_index.wxml
================================================
游玩指南
景区概况
服务游客 助力旅游
官方攻略
最具代表性的玩法
停车预约
景区停车更便捷
旅游人气榜
景点
人气榜
{{index+1}} {{item.title}}
攻略
人气榜
{{index+1}} {{item.title}}
================================================
FILE: miniprogram/projects/TRIP1/pages/default/index/default_index.wxss
================================================
@import "../../../style/skin.wxss";
.main {
padding: 0rpx 0rpx 100rpx;
}
.swipper {
width: 100%;
background-color: #4f5368;
}
.swipper image {
height: inherit;
width: inherit;
}
.menu {
width: 100%;
display: flex;
flex-wrap: wrap;
background-color: #fff;
padding: 20rpx 10rpx;
margin-top: -40rpx;
z-index: 99999;
border-top-left-radius: 40rpx;
border-top-right-radius: 40rpx;
}
.menu .item {
width: 25%;
display: flex;
align-items: center;
justify-content: center;
height: 180rpx;
}
.menu .item .item-inner {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.menu .item-inner .img {
width: 105rpx;
height: 105rpx;
display: flex;
background-color: #ccc;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.menu .item:nth-child(1) .item-inner .img {
background-image: linear-gradient(45deg, #10CB61, #7EE263);
}
.menu .item:nth-child(2) .item-inner .img {
background-image: linear-gradient(#68ebe4, #1cbbb4);
}
.menu .item:nth-child(3) .item-inner .img {
background-image: linear-gradient(45deg, #FE753B, #F8BF1B);
}
.menu .item:nth-child(4) .item-inner .img {
background-image: linear-gradient(#f5bedd, #e03997);
}
.menu .item-inner .img image {
width: 59rpx;
height: 59rpx;
}
.menu .item-inner .title {
margin-top: 20rpx;
font-size: 26rpx;
color: #4f5368;
}
.guide {
width: 100%;
display: flex;
flex-direction: column;
padding: 0rpx 20rpx 20rpx;
background-color: #fff;
margin-top: 20rpx;
}
.guide .title {
width: 100%;
font-size: 32rpx;
font-weight: bold;
padding: 30rpx 0rpx;
letter-spacing: 3rpx;
}
.guide .item {
width: 100%;
display: flex;
color: #fff;
}
.guide .item .left {
width: 350rpx;
background-image: linear-gradient(#6dab3f, #7BC541);
height: 350rpx;
border-radius: 20rpx;
padding: 30rpx 20rpx;
margin-right: 20rpx;
position: relative;
}
.guide .item .left image {
position: absolute;
top: 0;
left: 0;
width: inherit;
height: inherit;
border-radius: inherit;
z-index: 1;
}
.guide .item .left .line1 {
font-size: 38rpx;
margin-bottom: 20rpx;
z-index: 9;
position: relative;
}
.guide .item .left .line2 {
font-size: 28rpx;
z-index: 9;
position: relative;
}
.guide .item .right {
flex: 1;
display: flex;
flex-direction: column;
}
.guide .item .right .line {
width: 100%;
background-image: linear-gradient(45deg, #276eb0, #2B97FF);
border-radius: 20rpx;
padding: 20rpx 20rpx;
height: 165rpx;
position: relative;
}
.guide .item .right image {
position: absolute;
top: 0;
left: 0;
width: inherit;
height: inherit;
border-radius: inherit;
z-index: 1;
}
.guide .item .right .line:first-child {
margin-bottom: 20rpx;
}
.guide .item .right .line:last-child {
background-image: linear-gradient(-45deg, #c7771b, #F18E1B);
}
.guide .item .right .line .txt1 {
font-size: 32rpx;
margin-bottom: 10rpx;
z-index: 9;
position: relative;
}
.guide .item .right .line .txt2 {
font-size: 28rpx;
z-index: 9;
position: relative;
}
.hot {
width: 100%;
display: flex;
flex-direction: column;
padding: 0rpx 20rpx 20rpx;
background-color: #fff;
margin-top: 20rpx;
}
.hot .title {
width: 100%;
font-size: 32rpx;
font-weight: bold;
padding: 30rpx 0rpx;
letter-spacing: 3rpx;
}
.hot .item {
width: 100%;
display: flex;
align-items: flex-start;
}
.hot .item .left {
width: 150rpx;
display: flex;
background-color: #6dab3f;
height: 150rpx;
border-radius: 20rpx;
margin-right: 30rpx;
align-items: center;
justify-content: center;
flex-direction: column;
color: #fff;
position: relative;
}
.hot .item .left.bg1 {
background-image: linear-gradient(45deg, #276eb0, #2B97FF);
}
.hot .item .left.bg2 {
background-image: linear-gradient(#f5bedd, #e03997);
}
.hot .item .left image {
position: absolute;
top: 0;
left: 0;
width: inherit;
height: inherit;
border-radius: inherit;
opacity: .8;
}
.hot .item .left .line1 {
font-size: 32rpx;
z-index: 9;
}
.hot .item .left .line2 {
font-size: 28rpx;
margin-top: 10rpx;
z-index: 9;
}
.hot .item .right {
display: flex;
flex: 1;
flex-direction: column;
font-size: 30rpx;
color: #000;
justify-content: flex-start;
align-items: flex-start;
}
.hot .item .right .line {
width: 500rpx;
padding: 0rpx 0 14rpx;
height: 56rpx;
}
.hot .item .right .line .no {
margin-right: 10rpx;
font-weight: bold;
}
.hot .item .right .line:nth-child(1) .no {
color: #FEB361;
}
.hot .item .right .line:nth-child(2) .no {
color: #5DDE49;
}
.hot .item .right .line:nth-child(3) .no {
color: #70C6FE;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/calendar/meet_calendar.js
================================================
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const timeHelper = require('../../../../../helper/time_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
list: [],
day: '',
hasDays: []
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
},
_loadList: async function () {
let params = {
day: this.data.day
}
let opts = {
title: this.data.isLoad ? 'bar' : 'bar'
}
try {
this.setData({
list: null
});
await cloudHelper.callCloudSumbit('meet/list_by_day', params, opts).then(res => {
this.setData({
list: res.data,
isLoad: true
});
});
} catch (err) {
console.error(err);
}
},
_loadHasList: async function () {
let params = {
day: timeHelper.time('Y-M-D')
}
let opts = {
title: 'bar'
}
try {
await cloudHelper.callCloudSumbit('meet/list_has_day', params, opts).then(res => {
this.setData({
hasDays: res.data,
});
});
} catch (err) {
console.error(err);
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () {
this.setData({
day: timeHelper.time('Y-M-D')
}, async () => {
await this._loadHasList();
await this._loadList();
});
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadHasList();
await this._loadList();
wx.stopPullDownRefresh();
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
bindClickCmpt: async function (e) {
let day = e.detail.day;
this.setData({
day
}, async () => {
await this._loadList();
})
},
bindMonthChangeCmpt: function (e) {
},
url: async function (e) {
pageHelper.url(e, this);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/calendar/meet_calendar.json
================================================
{
"usingComponents": {
"cmpt-calendar": "/cmpts/public/calendar/calendar_comm/calendar_comm_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "预约日历"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/calendar/meet_calendar.wxml
================================================
本日没有可预约的项目哦~
{{item.title}}
{{item.timeDesc}}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/calendar/meet_calendar.wxss
================================================
@import "../../../style/skin.wxss";
.main {
padding: 0 0 20rpx;
}
.main .plan-date {
width: 100%;
position: sticky;
top: 0;
z-index: 999;
box-shadow: var(--ShadowSize) var(--greyShadow);
}
.main .list {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding: 30rpx 20rpx;
}
.main .list .item {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
height: 100rpx;
padding: 0 30rpx 0 0;
background-color: #fff;
margin-bottom: 30rpx;
border-radius: 10rpx;
overflow: hidden;
}
.main .list .item .left {
width: 8rpx;
height: 100rpx;
margin-right: 15rpx;
}
.main .list .item .img {
width: 65rpx;
height: 65rpx;
border-radius: 10rpx;
position: relative;
}
.main .list .item .title {
flex: 1;
text-align: left;
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-left: 20rpx;
}
.main .list .item .time {
width: 140rpx;
text-align: right;
font-size: 26rpx;
color: #999;
}
.main .list .no-project {
padding: 30rpx 30rpx;
font-size: 28rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/detail/meet_detail.js
================================================
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const MeetBiz = require('../../../biz/meet_biz.js');
const projectSetting = require('../../../public/project_setting.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const PassportBiz = require('../../../../../comm/biz/passport_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
tabCur: 0,
mainCur: 0,
verticalNavTop: 0,
showMind: true,
showTime: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
ProjectBiz.initPage(this);
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
_loadDetail: async function () {
let id = this.data.id;
if (!id) return;
let params = {
id,
};
let opt = {
title: 'bar'
};
let meet = await cloudHelper.callCloudData('meet/view', params, opt);
if (!meet) {
this.setData({
isLoad: null
})
return;
}
this.setData({
isLoad: true,
meet,
canNullTime: projectSetting.MEET_CAN_NULL_TIME
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
bindJoinTap: async function (e) {
if (!await PassportBiz.loginMustCancelWin(this)) return;
let dayIdx = pageHelper.dataset(e, 'dayidx');
let timeIdx = pageHelper.dataset(e, 'timeidx');
let time = this.data.meet.MEET_DAYS_SET[dayIdx].times[timeIdx];
if (time.error) {
if (time.error.includes('预约'))
return pageHelper.showModal('该时段' + time.error + ',换一个时段试试吧!');
else
return pageHelper.showModal('该时段预约' + time.error + ',换一个时段试试吧!');
}
let meetId = this.data.id;
let timeMark = time.mark;
let callback = async () => {
try {
let opts = {
title: '请稍候',
}
let params = {
meetId,
timeMark
}
await cloudHelper.callCloudSumbit('meet/before_join', params, opts).then(res => {
wx.navigateTo({
url: `../join/meet_join?id=${meetId}&timeMark=${timeMark}`,
})
});
} catch (ex) {
console.log(ex);
}
}
MeetBiz.subscribeMessageMeet(callback);
},
url: function (e) {
pageHelper.url(e, this);
},
onPageScroll: function (e) {
if (e.scrollTop > 100) {
this.setData({
topShow: true
});
} else {
this.setData({
topShow: false
});
}
},
bindTopTap: function () {
wx.pageScrollTo({
scrollTop: 0
})
},
bindVerticalMainScroll: function (e) {
if (!this.data.isLoad) return;
let list = this.data.meet.MEET_DAYS_SET;
let tabHeight = 0;
for (let i = 0; i < list.length; i++) {
let view = wx.createSelectorQuery().in(this).select("#main-" + i);
view.fields({
size: true
}, data => {
list[i].top = tabHeight;
tabHeight = tabHeight + data.height;
list[i].bottom = tabHeight;
}).exec();
}
let scrollTop = e.detail.scrollTop + 20; // + i*0.5; //TODO
for (let i = 0; i < list.length; i++) {
if (scrollTop > list[i].top && scrollTop < list[i].bottom) {
this.setData({
verticalNavTop: (i - 1) * 50,
tabCur: i
})
return false;
}
}
},
bindTabSelectTap: function (e) {
let idx = pageHelper.dataset(e, 'idx');
this.setData({
tabCur: idx,
mainCur: idx,
verticalNavTop: (idx - 1) * 50
})
},
bindShowMindTap: function (e) {
this.setData({
showMind: true,
showTime: false
});
},
bindShowTimeTap: function (e) {
this.setData({
showMind: false,
showTime: true
});
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/detail/meet_detail.json
================================================
{
"usingComponents": {
"cmpt-detail": "/cmpts/biz/detail/detail_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "预约详情"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/detail/meet_detail.wxml
================================================
{{meet.MEET_TITLE}}
{{item.val}}
暂无可预约时段,请选择其他
{{item.day}}
{{item.dayDesc}}
暂无可预约时段
{{itm.start}}~{{itm.end}}
({{itm.stat.succCnt}}/{{itm.limit}})
(人数不限)
(可预约)
({{itm.error}})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/detail/meet_detail.wxss
================================================
@import "../../../../../style/public/comm_box_list.wxss";
@import "../../../style/skin.wxss";
.main {
padding: 0rpx 0rpx !important;
}
.meet-no-time {
width: 100%;
background-color: #fff;
padding: 30rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 30rpx;
}
.meet-no-time {
width: 100%;
height: 100vh;
background-color: #fff;
padding: 80rpx 30rpx 30rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
font-size: 30rpx;
}
.meet-no-time text {
font-size: 90rpx;
}
/* 顶部区域 */
.top-area {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
background-color: #fff;
border-bottom: 2rpx solid #ccc;
position: fixed;
top: 0;
z-index: 999;
}
.top-area .top-title {
width: 100%;
font-size: 38rpx;
color: #000;
line-height: 110rpx;
padding: 0 20rpx;
font-weight: bold;
}
.top-area .top-menu {
width: 100%;
font-size: 28rpx;
color: #000;
line-height: 80rpx;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
background-color: #f8f8f8;
}
.top-area .top-menu .item {
padding: 0 20rpx;
position: relative;
}
.top-area .top-menu .item.cur::after {
position: absolute;
bottom: 0;
left: 76rpx;
width: 60rpx;
text-align: center;
height: 5rpx;
background-color: var(--projectColor);
content: " ";
animation: hightlightning_in 0.3s ease-out;
transform-origin: left;
}
@keyframes hightlightning_in {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
.big-box {
width: 100%;
padding: 0;
margin-top: 193rpx;
display: flex;
}
/*** 详情盒子 ***/
.article-box {
box-sizing: border-box;
width: 100%;
box-sizing: border-box;
padding: 30rpx 20rpx;
}
.article-box .article {
background-color: #fff;
padding: 10rpx 30rpx;
border-radius: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.article-box .article .info {
color: #333;
margin: 0rpx 0rpx;
box-sizing: border-box;
width: 100%;
line-height: 1.6;
display: flex;
justify-content: flex-start;
align-items: flex-start;
padding: 10rpx 0rpx 10rpx;
}
.article-box .article .info .info-left {
width: 50rpx;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.article-box .article .info .info-right {
height: 100%;
flex: 1;
font-size: 26rpx;
}
.article-box .article .info .info-end {
height: 100%;
width: 40rpx;
font-size: 26rpx;
color: #ccc;
display: flex;
justify-content: flex-start;
align-items: center;
}
.article-box .article .content {
margin-top: 24rpx;
color: #333;
font-size: 28rpx;
text-align: justify;
line-height: 1.6;
}
.article-box .article .pics {
margin-top: 30rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.article-box .article .pics image {
width:100%;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
/* 垂直导航 */
.nav {
white-space: nowrap;
}
::-webkit-scrollbar {
display: none;
}
.nav .item {
display: inline-block;
margin: 0 10rpx;
padding: 0 20rpx;
}
.nav .item.cur {
border-bottom: 4rpx solid;
}
.vertical-nav.nav {
width: 200rpx;
white-space: initial;
}
.vertical-nav.nav .item {
width: 100%;
text-align: center;
margin: 0;
border: none;
height: 50px;
line-height: 50px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.vertical-nav.nav .item.cur {
background-color: #f1f1f1;
font-weight: bold;
}
.vertical-nav.nav .item.cur::after {
content: "";
width: 8rpx;
height: 30rpx;
border-radius: 10rpx 0 0 10rpx;
position: absolute;
background-color: currentColor;
top: 0;
right: 0rpx;
bottom: 0;
margin: auto;
}
.vertical-box {
display: flex;
width: 100%;
background-color: #fff;
}
.vertical-main {
background-color: #f1f1f1;
flex: 1;
}
.vertical-main .vertical-main-box {
padding: 30rpx 30rpx 0rpx 30rpx;
width: 100%;
}
.vertical-main .righ-tab-bar {
width: 100%;
text-align: left;
}
.vertical-main .right-tab-tilte {
font-weight: bold;
}
.vertical-main .text-list {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
background-color: #fff;
padding-bottom: 20rpx;
padding: 0rpx 10rpx 20rpx 10rpx;
padding-top: 10rpx;
}
.vertical-main .text-list .item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 28rpx;
color: #333;
width: 50%;
text-align: center;
padding: 10rpx 10rpx;
}
.vertical-main .text-list .item .time-text {
width:100%;
border-radius: 15rpx;
border: 1rpx solid #ccc;
background-color: #f8f8f8;
height: 65rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 28rpx;
color:#000;
}
.vertical-main .text-list .item .time-text.error {
background-color: #f1f1f1;
color: #aaa;
}
.vertical-main .text-list .item .time-text.limit {
height: 95rpx;
}
.vertical-main .text-list .item .time-text .limit-text {
width: 100%;
font-size: 24rpx;
color: #999;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/index/meet_index.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const MeetBiz = require('../../../biz/meet_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (options && options.id) {
this.setData({
_params: {
typeId: options.id,
}
});
MeetBiz.setTypeTitle(this, options.id);
} else {
// 默认1
this.setData({
_params: {
typeId: 1,
}
});
MeetBiz.setTypeTitle(this, 1);
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/index/meet_index.json
================================================
{
"usingComponents": {
},
"disableScroll": true,
"navigationBarTitleText": "预约列表"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/index/meet_index.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/index/meet_index.wxss
================================================
@import "../../../../../style/public/comm_box_list.wxss";
@import "../../../style/skin.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/join/meet_join.js
================================================
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const MeetBiz = require('../../../biz/meet_biz.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const PassportBiz = require('../../../../../comm/biz/passport_biz.js');
const projectSetting = require('../../../public/project_setting.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
forms: [],
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (!pageHelper.getOptions(this, options)) return;
if (!pageHelper.getOptions(this, options, 'timeMark')) return;
if (!await PassportBiz.loginMustBackWin(this)) return;
this._loadDetail();
},
_loadDetail: async function () {
let id = this.data.id;
if (!id) return;
let timeMark = this.data.timeMark;
if (!timeMark) return;
let params = {
meetId: id,
timeMark
};
let opt = {
title: 'bar'
};
let meet = await cloudHelper.callCloudData('meet/detail_for_join', params, opt);
if (!meet) {
this.setData({
isLoad: null
})
return;
}
this.setData({
isLoad: true,
meet,
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
url: function (e) {
pageHelper.url(e, this);
},
onPageScroll: function (e) {
// 回页首按钮
pageHelper.showTopBtn(e, this);
},
bindCheckTap: async function (e) {
this.selectComponent("#form-show").checkForms();
},
bindSubmitCmpt: async function (e) {
let forms = e.detail;
let callback = async () => {
try {
let opts = {
title: '提交中'
}
let params = {
meetId: this.data.id,
timeMark: this.data.timeMark,
forms
}
await cloudHelper.callCloudSumbit('meet/join', params, opts).then(res => {
let content = '预约成功!'
let joinId = res.data.joinId;
wx.showModal({
title: '温馨提示',
showCancel: false,
content,
success() {
let ck = () => {
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/meet/my_join_detail/meet_my_join_detail?flag=home&id=' + joinId),
})
}
ck();
}
})
})
} catch (err) {
console.log(err);
};
}
// 消息订阅
await MeetBiz.subscribeMessageMeet(callback);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/join/meet_join.json
================================================
{
"usingComponents": {
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "预约登记"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/join/meet_join.wxml
================================================
{{meet.MEET_TITLE}}
{{meet.dayDesc}} 更改时段
请您填写资料,带*号为必填项
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/join/meet_join.wxss
================================================
@import "../../../../../style/public/detail.wxss";
@import "../../../style/skin.wxss";
.form-group .line-desc {
font-size: 28rpx;
text-align: center;
width: 100%;
color: #666;
}
.submit-line {
width: 100%;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_detail/meet_my_join_detail.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const timeHelper = require('../../../../../helper/time_helper.js');
const qrcodeLib = require('../../../../../lib/tools/qrcode_lib.js');
const MeetBiz = require('../../../biz/meet_biz.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
isShowHome: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
ProjectBiz.initPage(this);
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
if (options && options.flag == 'home') {
this.setData({
isShowHome: true
});
}
},
_loadDetail: async function (e) {
let id = this.data.id;
if (!id) return;
let params = {
joinId: id
}
let opts = {
title: 'bar'
}
try {
let join = await cloudHelper.callCloudData('meet/my_join_detail', params, opts);
if (!join) {
this.setData({
isLoad: null
})
return;
}
let qrImageData = qrcodeLib.drawImg('meet=' + join.JOIN_CODE, {
typeNumber: 1,
errorCorrectLevel: 'L',
size: 100
});
this.setData({
isLoad: true,
join,
qrImageData
});
} catch (err) {
console.error(err);
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
bindCancelTap: async function (e) {
let callback = async () => {
try {
let params = {
joinId: this.data.id
}
let opts = {
title: '取消中'
}
await cloudHelper.callCloudSumbit('meet/my_join_cancel', params, opts).then(res => {
let join = this.data.join;
join.JOIN_STATUS = 10;
this.setData({
join
});
pageHelper.showNoneToast('已取消');
});
} catch (err) {
console.log(err);
}
}
pageHelper.showConfirm('确认取消该预约?', callback);
},
url: function (e) {
pageHelper.url(e, this);
},
bindNoticeTap: function (e) {
let callback = () => {
pageHelper.showSuccToast('开启成功');
}
MeetBiz.subscribeMessageMeet(callback);
},
bindCalendarTap: function (e) {
let join = this.data.join;
let title = join.JOIN_MEET_TITLE;
let startTime = timeHelper.time2Timestamp(join.JOIN_MEET_DAY + ' ' + join.JOIN_MEET_TIME_START + ':00') / 1000;
let endTime = timeHelper.time2Timestamp(join.JOIN_MEET_DAY + ' ' + join.JOIN_MEET_TIME_END + ':00') / 1000;
pageHelper.addPhoneCalendar(title, startTime, endTime);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_detail/meet_my_join_detail.json
================================================
{
"usingComponents": {
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的预约详情"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_detail/meet_my_join_detail.wxml
================================================
{{join.JOIN_MEET_TITLE}}
{{join.JOIN_MEET_DAY}} {{join.JOIN_MEET_TIME_START}}~{{join.JOIN_MEET_TIME_END}}
预约成功{{join.JOIN_IS_CHECKIN==1?',已签到':''}}
已取消
系统取消:{{join.JOIN_REASON}}
返回首页
加入手机日程
预约码 (向工作人员出示进行核销)
预约信息
{{item.title}}:
{{item.val}}
提交时间:{{join.JOIN_ADD_TIME}}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_detail/meet_my_join_detail.wxss
================================================
@import "../../../../../style/public/article_list.wxss";
@import "../../../style/skin.wxss";
.main {
padding: 0;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.text-pic-list-box {
padding: 20rpx 0rpx 10rpx;
}
.text-pic-list-box .item {
padding: 20rpx 30rpx 20rpx 30rpx;
}
.text-pic-list-box .title-line {
width: 100%;
justify-content: space-between;
display: flex;
align-items: flex-start;
}
.text-pic-list-box .title-line .arrow {
width: 100rpx;
color: #666;
text-align: right;
}
.text-pic-list-box .item .oprt {
line-height: 1.5;
font-size: 27rpx;
margin: 30rpx 0 10rpx;
display: flex;
justify-content: center;
align-items: center;
color: var(--projectColor)
}
.text-pic-list-box .item .oprt .btn {
padding: 0 20rpx;
}
.info-list-box {
width: 100%;
box-sizing: border-box;
padding: 0rpx 0rpx 10rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.info-list-box .item {
padding: 20rpx 30rpx 20rpx 40rpx;
background-color: #fff;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 20rpx;
position: relative;
}
.info-list-box .item .info {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
font-size: 28rpx;
line-height: 1.5;
color: #333;
border-bottom: 2rpx dotted #ccc;
padding: 10rpx 0;
}
.info-list-box .item .info:last-child,
.info-list-box .item .info:first-child {
border-bottom: 0;
}
.info-list-box .item .info .title {
font-weight: bold;
width: 150rpx;
margin-right: 10rpx;
align-self: start;
position: relative;
}
.info-list-box .item .info .content {
flex: 1;
display: flex;
align-items: flex-start;
justify-content: flex-start;
align-self: start;
}
.info-list-box .item .info .center {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.info-list-box .item .info .center image {
width: 400rpx;
height: 400rpx;
margin: 30rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_list/meet_my_join_list.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLogin: true
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
ProjectBiz.initPage(this);
this._getSearchMenu();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
/** 搜索菜单设置 */
_getSearchMenu: function () {
let sortItem1 = [{
label: '排序',
type: '',
value: ''
}, {
label: '按时间倒序',
type: 'timedesc',
value: ''
}, {
label: '按时间正序',
type: 'timeasc',
value: ''
}];
let sortItems = [sortItem1];
let sortMenus = [{
label: '全部',
type: '',
value: ''
}, {
label: '今日',
type: 'today',
value: ''
}, {
label: '明日',
type: 'tomorrow',
value: ''
}, {
label: '已预约',
type: 'succ',
value: ''
},
{
label: '已取消',
type: 'cancel',
value: ''
}
]
this.setData({
sortItems,
sortMenus
});
},
bindCancelTap: async function (e) {
let callback = async () => {
let joinId = pageHelper.dataset(e, 'id');
try {
let params = {
joinId
}
let opts = {
title: '取消中'
}
await cloudHelper.callCloudSumbit('meet/my_join_cancel', params, opts).then(res => {
pageHelper.modifyListNode(joinId, this.data.dataList.list, 'JOIN_STATUS', 10, '_id');
this.setData({
dataList: this.data.dataList
});
pageHelper.showNoneToast('已取消');
});
} catch (err) {
console.log(err);
}
}
pageHelper.showConfirm('确认取消该预约?', callback);
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_list/meet_my_join_list.json
================================================
{
"usingComponents": {
},
"disableScroll": true,
"navigationBarTitleText": "我的预约"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_list/meet_my_join_list.wxml
================================================
已结束
{{item.JOIN_MEET_TITLE}}
{{item.JOIN_MEET_DAY}} {{item.JOIN_MEET_TIME_START}}~{{item.JOIN_MEET_TIME_END}}
预约成功{{item.JOIN_IS_CHECKIN==1?',已签到':''}}
已取消
系统取消:{{item.JOIN_REASON}}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/my_join_list/meet_my_join_list.wxss
================================================
@import "../../../../../style/public/article_list.wxss";
@import "../../../style/skin.wxss";
.text-pic-list-box .item {
padding: 20rpx 30rpx 20rpx 40rpx;
position: relative;
overflow: hidden;
}
.text-pic-list-box .item .status {
z-index: 9999;
position: absolute;
top: 0;
right: 0;
font-size: 24rpx;
border-bottom-left-radius: 10rpx;
background-color: #f8f8f8;
padding: 0rpx 10rpx 0rpx;
color: #bbb;
}
.text-pic-list-box .item .qr {
z-index: 9999;
position: absolute;
top: 0;
right: 0;
font-size: 24rpx;
border-bottom-left-radius: 10rpx;
background-color: #f2f2f2;
padding: 0rpx 10rpx 0rpx;
color: #666;
font-size:40rpx;
}
.btn.mid {
padding: 0 30rpx;
}
.text-pic-list-box .item .author {
font-size: 28rpx;
color: #999;
}
.text-pic-list-box .item .title {
position: relative;
padding-right: 20rpx;
}
.text-pic-list-box .item .data {
line-height: 1.5;
font-size: 26rpx;
color: #666;
margin-top: 10rpx;
display: flex;
justify-content: flex-start;
align-items: center;
}
.text-time {
color: #ccc;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/self/meet_self.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (options && options.scene) {
let params = {
timeMark: options.scene
};
let opts = {
title: 'bar'
}
try {
await cloudHelper.callCloudSumbit('meet/my_join_checkin', params, opts).then(res => {
let cb = () => {
wx.reLaunch({
url: pageHelper.fmtURLByPID('/pages/my/index/my_index'),
});
}
pageHelper.showModal(res.data.ret, '温馨提示', cb);
});
} catch (err) {
console.error(err);
}
} else {
pageHelper.showModal('签到码扫描错误,请关闭本小程序,使用「微信›扫一扫」重新扫码');
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/self/meet_self.json
================================================
{
"usingComponents": {
},
"navigationBarTitleText": "自助签到"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/self/meet_self.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/meet/self/meet_self.wxss
================================================
@import "../../../style/skin.wxss";
.load.loading::after {
content: "自助签到中,请稍候...";
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/edit/my_edit.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const validate = require('../../../../../helper/validate.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const projectSetting = require('../../../public/project_setting.js');
const setting = require('../../../../../setting/setting.js');
const PassportBiz = require('../../../../../comm/biz/passport_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
isEdit: true,
userRegCheck: projectSetting.USER_REG_CHECK,
mobileCheck: setting.MOBILE_CHECK
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
await this._loadDetail();
},
_loadDetail: async function (e) {
let opts = {
title: 'bar'
}
let user = await cloudHelper.callCloudData('passport/my_detail', {}, opts);
if (!user)
return wx.redirectTo({ url: '../reg/my_reg' });
this.setData({
isLoad: true,
isEdit: true,
user,
fields: projectSetting.USER_FIELDS,
formName: user.USER_NAME,
formMobile: user.USER_MOBILE,
formForms: user.USER_FORMS
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
bindGetPhoneNumber: async function (e) {
await PassportBiz.getPhone(e, this);
},
bindSubmitTap: async function (e) {
try {
let data = this.data;
// 数据校验
data = validate.check(data, PassportBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
let opts = {
title: '提交中'
}
await cloudHelper.callCloudSumbit('passport/edit_base', data, opts).then(res => {
let callback = () => {
wx.reLaunch({ url: '../index/my_index' });
}
pageHelper.showSuccToast('修改成功', 1500, callback);
});
} catch (err) {
console.error(err);
}
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/my/edit/my_edit.json
================================================
{
"usingComponents": {
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "个人资料"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/edit/my_edit.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/my/edit/my_edit.wxss
================================================
@import "../../../style/skin.wxss";
.submit-line {
width: 100%;
}
.form-group .mobile {
flex: 1;
text-align: left;
}
.submit-line {
width: 100%;
}
.form-group .mobile {
flex: 1;
text-align: left;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/edit/user_form.wxml
================================================
审核未通过:请修改资料后重新提交
审核未通过:{{user.USER_CHECK_REASON}}。请修改资料后重新提交
您的注册资料已经提交,请耐心等待审核~
昵称
{{formNameFocus}}
手机
{{formMobile||'未填写'}}
{{formMobileFocus}}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/fav/my_fav.js
================================================
const behavior = require('../../../../../comm/behavior/my_fav_bh.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
behaviors: [behavior],
onReady: function () {
ProjectBiz.initPage(this);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/my/fav/my_fav.json
================================================
{
"usingComponents": {},
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的收藏"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/fav/my_fav.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/my/fav/my_fav.wxss
================================================
@import "../../../../../style/public/article_list.wxss";
@import "../../../style/skin.wxss";
@import "../../../../../style/project/my_fav_style.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/my/foot/my_foot.js
================================================
const behavior = require('../../../../../comm/behavior/my_foot_bh.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
behaviors: [behavior],
onReady: function () {
ProjectBiz.initPage(this);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/my/foot/my_foot.json
================================================
{
"usingComponents": {},
"enablePullDownRefresh": true,
"navigationBarTitleText": "历史浏览"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/foot/my_foot.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/my/foot/my_foot.wxss
================================================
@import "../../../../../style/public/article_list.wxss";
@import "../../../style/skin.wxss";
@import "../../../../../style/project/my_foot_style.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/my/index/my_index.js
================================================
/**
* Ver : CCMiniCloud Framework 2.0.1 ALL RIGHTS RESERVED BY cclinux0730 (wechat)
* Date: 2020-10-29 07:48:00
*/
const cacheHelper = require('../../../../../helper/cache_helper.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const AdminBiz = require('../../../../../comm/biz/admin_biz.js');
const setting = require('../../../../../setting/setting.js');
const PassportBiz = require('../../../../../comm/biz/passport_biz.js');
Page({
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
if (PassportBiz.isLogin()) {
let user = {};
user.USER_NAME = PassportBiz.getUserName();
this.setData({ user });
}
ProjectBiz.initPage(this);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () {
PassportBiz.clearToken();
PassportBiz.loginSilence(this);
this._loadUser();
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
_loadUser: async function (e) {
let opts = {
title: 'bar'
}
let user = await cloudHelper.callCloudData('passport/my_detail', {}, opts);
if (!user) {
this.setData({
user: null
});
return;
}
this.setData({
user
})
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadUser();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () { },
url: function (e) {
pageHelper.url(e, this);
},
bindSetTap: function (e, skin) {
let itemList = ['清除缓存', '后台管理'];
wx.showActionSheet({
itemList,
success: async res => {
let idx = res.tapIndex;
if (idx == 0) {
cacheHelper.clear();
pageHelper.showNoneToast('清除缓存成功');
}
if (idx == 1) {
if (setting.IS_SUB) {
AdminBiz.adminLogin(this, 'admin', '123456');
} else {
wx.reLaunch({
url: '../../admin/index/login/admin_login',
});
}
}
},
fail: function (res) { }
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/my/index/my_index.json
================================================
{
"usingComponents": {
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/index/my_index.wxml
================================================
{{user?user.USER_NAME:'欢迎回来~~~'}}
已禁用
已注册,待审核
审核未通过,请修改资料
{{user?'欢迎回来~~~':'马上注册,使用更多功能'}}
修改我的个人资料
我的预约
我的收藏
历史浏览
系统后台管理(点击试用)
关于我们
联系作者了解更多详情
设置
================================================
FILE: miniprogram/projects/TRIP1/pages/my/index/my_index.wxss
================================================
@import "../../../style/skin.wxss";
.main {
padding: 0;
}
.main .upside {
background-image: linear-gradient( #00C176, #08dc64);
height: 250rpx;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
border-bottom-left-radius: 80rpx;
}
.main .upside .upImg {
height: inherit;
width: 100%;
position: absolute;
top: 0;
left: 0;
}
.main .upside .user-bar {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
padding-left: 40rpx;
z-index: 9999;
}
.main .upside .user-bar .avatar {
height: 120rpx;
width: 120rpx;
border: 0rpx;
font-size: 80rpx;
border-radius: 50%;
background-color: #f8f8f8;
display: flex;
align-items: center;
justify-content: center;
}
.main .upside .user-bar .avatar image {
height: 70rpx;
width: 70rpx;
}
.main .upside .user-bar .detail {
width: 500rpx;
margin-left: 0rpx;
margin-right: 40rpx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
color: #fff;
box-sizing: border-box;
}
.main .upside .user-bar .detail .name {
width: 100%;
font-size: 36rpx;
font-weight: bold;
color: #fff;
}
.main .upside .user-bar .detail .desc {
width: 100%;
font-size: 26rpx;
margin-top: 15rpx;
color: #fff;
}
.main .down {
box-sizing: border-box;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 20rpx 0rpx !important;
margin-top: 20rpx;
}
.main .down .data {
display: flex;
justify-content: space-between;
align-items: center;
height: 150rpx;
padding: 0rpx 40rpx 0rpx 40rpx;
width: 100%;
background-color: #fff;
}
.main .down .data view {
width: 150rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.main .down .data view .num {
font-size: 32rpx;
font-weight: bold;
}
.main .down .data view .txt {
font-size: 24rpx;
color: #aaa;
}
.main .comm-list .item.arrow::before,
.main .comm-list .item .content .item-icon {
color: #0E9489;
}
.main .down .today {
width: 100%;
padding: 30rpx 30rpx 20rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff;
}
.main .down .today .title {
width: 100%;
text-align: left;
font-size: 32rpx;
font-weight: bold;
line-height: 1.2;
margin-bottom: 30rpx;
}
.main .down .today .list {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
}
.main .down .today .list .item {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
padding: 15rpx 0;
border-bottom: 1rpx dashed #ccc;
}
.main .down .today .list .item:last-child {
margin-bottom: 0rpx;
border: 0;
}
.main .down .today .list .item .time {
font-size: 28rpx;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
line-height: 2;
font-weight: bold;
}
.main .down .today .list .item .time .status {
font-weight: normal;
font-size: 27rpx;
}
.main .down .today .list .item .project {
color: #666;
font-size: 24rpx;
width: 100%;
text-align: left;
}
.main .site-footer {
width: 100%;
align-self: flex-end;
}
.main .upside-shadow {
box-shadow: unset;
}
.main .upside-shadow:before,
.main .upside-shadow:after {
position: absolute;
content: "";
top: 20rpx;
bottom: 30rpx;
left: 20rpx;
width: 50%;
box-shadow: 0 30rpx 20rpx rgba(0, 0, 0, 0.2);
transform: rotate(-3deg);
z-index: -1;
}
.main .upside-shadow:after {
right: 20rpx;
left: auto;
transform: rotate(3deg);
}
.down .comm-list.menu.card-project {
border-radius: 0rpx !important;
}
.shadow-project {
box-shadow: unset;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/reg/my_reg.js
================================================
const pageHelper = require('../../../../../helper/page_helper.js');
const helper = require('../../../../../helper/helper.js');
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const validate = require('../../../../../helper/validate.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const projectSetting = require('../../../public/project_setting.js');
const setting = require('../../../../../setting/setting.js');
const PassportBiz = require('../../../../../comm/biz/passport_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
isEdit: false,
mobileCheck: setting.MOBILE_CHECK
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (options && options.retUrl)
this.data.retUrl = decodeURIComponent(options.retUrl);
await this._loadDetail();
},
_loadDetail: async function (e) {
let opts = {
title: 'bar'
}
let user = await cloudHelper.callCloudData('passport/my_detail', {}, opts);
if (user) {
return wx.redirectTo({ url: '../index/my_index' });
}
this.setData({
isLoad: true,
fields: projectSetting.USER_FIELDS,
formName: '',
formMobile: '',
formForms: []
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
bindGetPhoneNumber: async function (e) {
PassportBiz.getPhone(e, this);
},
bindSubmitTap: async function (e) {
try {
let data = this.data;
// 数据校验
data = validate.check(data, PassportBiz.CHECK_FORM, this);
if (!data) return;
let forms = this.selectComponent("#cmpt-form").getForms(true);
if (!forms) return;
data.forms = forms;
data.status = projectSetting.USER_REG_CHECK ? 0 : 1;
let opts = {
title: '提交中'
}
await cloudHelper.callCloudSumbit('passport/register', data, opts).then(result => {
if (result && helper.isDefined(result.data.token) && result.data.token) {
// 用户需要审核,不能登录
if (!projectSetting.USER_REG_CHECK) PassportBiz.setToken(result.data.token);
let callback = () => {
if (this.data.retUrl == 'back')
wx.navigateBack();
else if (this.data.retUrl)
wx.redirectTo({
url: this.data.retUrl,
})
else
wx.reLaunch({ url: '../index/my_index' });
}
if (projectSetting.USER_REG_CHECK)
pageHelper.showModal('注册完成,等待系统审核', '温馨提示', callback);
else
pageHelper.showSuccToast('注册成功', 1500, callback);
}
});
} catch (err) {
console.error(err);
}
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/my/reg/my_reg.json
================================================
{
"usingComponents": {
"cmpt-form-show": "/cmpts/public/form/form_show/form_show_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "注册"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/my/reg/my_reg.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/my/reg/my_reg.wxss
================================================
@import "../../../style/skin.wxss";
.submit-line {
width: 100%;
}
.form-group .mobile {
flex: 1;
text-align: left;
}
.submit-line {
width: 100%;
}
.form-group .mobile {
flex: 1;
text-align: left;
font-size:28rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate1/news_cate1.js
================================================
let behavior = require('../../../../../comm/behavior/news_index_bh.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const projectSetting = require('../../../public/project_setting.js');
Page({
behaviors: [behavior],
onLoad: function (options) {
ProjectBiz.initPage(this);
this._setCate(projectSetting.NEWS_CATE, options, 1);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate1/news_cate1.json
================================================
{
"usingComponents": {
},
"disableScroll": true,
"navigationBarTitleText": "资讯"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate1/news_cate1.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate1/news_cate1.wxss
================================================
@import "../../../../../style/public/comm_box_list.wxss";
@import "../../../style/skin.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate2/news_cate2.js
================================================
let behavior = require('../../../../../comm/behavior/news_index_bh.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const projectSetting = require('../../../public/project_setting.js');
Page({
behaviors: [behavior],
onLoad: function (options) {
ProjectBiz.initPage(this);
this._setCate(projectSetting.NEWS_CATE, options, 3);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate2/news_cate2.json
================================================
{
"usingComponents": {
},
"disableScroll": true,
"navigationBarTitleText": "资讯"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate2/news_cate2.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/news/cate2/news_cate2.wxss
================================================
@import "../../../../../style/public/comm_box_list.wxss";
@import "../../../style/skin.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/news/detail/news_detail.js
================================================
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
_loadDetail: async function () {
let id = this.data.id;
if (!id) return;
let params = {
id,
};
let opt = {
title: 'bar'
};
let news = await cloudHelper.callCloudData('news/view', params, opt);
if (!news) {
this.setData({
isLoad: null
})
return;
}
this.setData({
isLoad: true,
news,
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
url: function (e) {
pageHelper.url(e, this);
},
onPageScroll: function (e) {
// 回页首按钮
pageHelper.showTopBtn(e, this);
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function (res) {
return {
title: this.data.news.NEWS_TITLE,
imageUrl: this.data.news.NEWS_PIC[0]
}
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/news/detail/news_detail.json
================================================
{
"usingComponents": {
"cmpt-detail": "/cmpts/biz/detail/detail_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "详细内容"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/news/detail/news_detail.wxml
================================================
{{news.NEWS_TITLE}}
{{news.NEWS_ADD_TIME}} {{news.NEWS_CATE_NAME}}
{{item.val}}
================================================
FILE: miniprogram/projects/TRIP1/pages/news/detail/news_detail.wxss
================================================
@import "../../../../../style/public/detail.wxss";
@import "../../../style/skin.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/news/index/news_index.js
================================================
let behavior = require('../../../../../comm/behavior/news_index_bh.js');
const ProjectBiz = require('../../../biz/project_biz.js');
const projectSetting = require('../../../public/project_setting.js');
Page({
behaviors: [behavior],
onLoad: function (options) {
ProjectBiz.initPage(this);
this._setCate(projectSetting.NEWS_CATE, options);
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/news/index/news_index.json
================================================
{
"usingComponents": {
},
"disableScroll": true,
"navigationBarTitleText": "资讯"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/news/index/news_index.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/news/index/news_index.wxss
================================================
@import "../../../../../style/public/comm_box_list.wxss";
@import "../../../style/skin.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/product/detail/product_detail.js
================================================
const cloudHelper = require('../../../../../helper/cloud_helper.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const ProjectBiz = require('../../../biz/project_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
isLoad: false,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
if (!pageHelper.getOptions(this, options)) return;
this._loadDetail();
},
_loadDetail: async function () {
let id = this.data.id;
if (!id) return;
let params = {
id,
};
let opt = {
title: 'bar'
};
let product = await cloudHelper.callCloudData('product/view', params, opt);
if (!product) {
this.setData({
isLoad: null
})
return;
}
this.setData({
isLoad: true,
product,
});
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: async function () {
await this._loadDetail();
wx.stopPullDownRefresh();
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () { },
url: function (e) {
pageHelper.url(e, this);
},
onPageScroll: function (e) {
// 回页首按钮
pageHelper.showTopBtn(e, this);
},
onShareAppMessage: function (res) {
return {
title: this.data.product.PRODUCT_TITLE,
imageUrl: this.data.product.PRODUCT_OBJ.cover[0]
}
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/product/detail/product_detail.json
================================================
{
"usingComponents": {
"cmpt-swiper": "/cmpts/public/swiper/swiper_cmpt",
"cmpt-detail": "/cmpts/biz/detail/detail_cmpt"
},
"enablePullDownRefresh": true,
"navigationBarTitleText": "景点详情"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/product/detail/product_detail.wxml
================================================
{{product.PRODUCT_TITLE}}
{{product.PRODUCT_OBJ.time}}
{{product.PRODUCT_OBJ.address}}
简介
{{product.PRODUCT_OBJ.desc}}
交通攻略
{{product.PRODUCT_OBJ.traffic}}
门票攻略
{{product.PRODUCT_OBJ.ticket}}
其他信息
{{product.PRODUCT_OBJ.other}}
================================================
FILE: miniprogram/projects/TRIP1/pages/product/detail/product_detail.wxss
================================================
@import "../../../style/skin.wxss";
.main {
width: 100%;
padding: 0;
}
.header {
width: 100%;
display: flex;
flex-direction: column;
background-color: #fff;
padding: 20rpx 30rpx;
}
.header .line1 {
width: 100%;
font-weight: bold;
font-size: 36rpx;
color: #000;
margin-bottom: 15rpx;
}
.header .line2,
.header .line3 {
width: 100%;
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
padding: 10rpx 0rpx;
}
.header .line2 {
border-bottom: 1rpx solid #eee;
}
.content {
margin-top: 20rpx;
width: 100%;
display: flex;
flex-direction: column;
background-color: #fff;
padding: 20rpx 30rpx;
margin-bottom: 200rpx;
}
.content .item {
padding: 0rpx 0;
width: 100%;
display: flex;
flex-direction: column;
}
.content .item .title {
width: 100%;
font-size: 30rpx;
color: #000;
font-weight: bold;
border-bottom: 1rpx solid #eee;
padding: 20rpx 0;
margin-bottom: 20rpx;
}
.content .item .desc {
width: 100%;
font-size: 28rpx;
color: #666;
margin-bottom: 40rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/product/index/product_index.js
================================================
const ProjectBiz = require('../../../biz/project_biz.js');
const pageHelper = require('../../../../../helper/page_helper.js');
const ProductBiz = require('../../../biz/product_biz.js');
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: async function (options) {
ProjectBiz.initPage(this);
this._getSearchMenu();
if (options && options.id) {
this.setData({
_params: {
sortType: 'cateId',
sortVal: options.id,
}
});
} else {
this.setData({
_params: {
sortType: 'cateId',
sortVal: '',
}
});
}
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: async function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
url: async function (e) {
pageHelper.url(e, this);
},
bindCommListCmpt: function (e) {
pageHelper.commListListener(this, e);
},
onShareAppMessage: function () {
},
_getSearchMenu: function () {
ProductBiz.setCateTitle();
let sortItem1 = [{ label: '全部', type: 'cateId', value: '' }];
sortItem1 = sortItem1.concat(ProductBiz.getCateList());
let sortItems = [
{ label: '排序', type: '', value: '' },
{ label: '最新', type: 'sort', value: 'PRODUCT_ADD_TIME|desc' },
{ label: '最热', type: 'sort', value: 'PRODUCT_VIEW_CNT|desc' },
{ label: '推荐指数', type: 'sort', value: 'PRODUCT_OBJ.star|desc' },
];
let sortMenus = sortItem1;
this.setData({
sortItems: [sortItems],
sortMenus
})
}
})
================================================
FILE: miniprogram/projects/TRIP1/pages/product/index/product_index.json
================================================
{
"usingComponents": {
},
"disableScroll": true,
"navigationBarTitleText": "景点"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/product/index/product_index.wxml
================================================
module.exports = {
parseInt: parseInt,
}
{{item.PRODUCT_TITLE}}
{{item.PRODUCT_OBJ.desc}}
================================================
FILE: miniprogram/projects/TRIP1/pages/product/index/product_index.wxss
================================================
@import "../../../style/skin.wxss";
.product-list {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding: 10rpx 0rpx;
}
.product-list .item {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: flex-start;
position: relative;
padding: 15rpx 0;
background-color: #fff;
margin-bottom: 10rpx;
}
.product-list .item image {
width: 200rpx;
height: 150rpx;
border-radius: 10rpx;
margin-right: 20rpx;
margin-left: 20rpx;
position: relative;
overflow: hidden;
}
.product-list .item .star {
position: absolute;
top: 5rpx;
left: 5rpx;
color: #fbbd08;
font-size: 22rpx;
}
.product-list .item .right {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
height: 160rpx;
}
.product-list .item .right .line1 {
width: 100%;
font-size: 32rpx;
color: #000;
font-weight: bold;
margin-bottom: 10rpx;
}
.product-list .item .right .line2 {
width: 100%;
font-size: 34rpx;
color: var(--projectColor);
font-weight: bold;
flex: 1;
padding-top: 10rpx;
}
.product-list .item .right .line3 {
font-size: 28rpx;
font-weight: normal;
color: #666;
line-height: 1.5;
width: 90%;
}
.product-list .item .appt {
position: absolute;
bottom: 20rpx;
right: 15rpx;
width: 130rpx;
background-color: var(--projectColor);
line-height: 2.1;
border-radius: 25rpx;
text-align: center;
color: #fff;
font-size: 24rpx;
}
================================================
FILE: miniprogram/projects/TRIP1/pages/search/search.js
================================================
const behavior = require('../../../../comm/behavior/search_bh.js');
const ProjectBiz = require('../../biz/project_biz.js');
const pageHelper = require('../../../../helper/page_helper.js');
Page({
behaviors: [behavior],
onReady: function () {
ProjectBiz.initPage(this);
let curPage = pageHelper.getPrevPage(1);
if (!curPage) return;
if (curPage.options && curPage.options.source == 'admin') {
wx.setNavigationBarColor({ //管理端顶部
backgroundColor: '#2499f2',
frontColor: '#ffffff',
});
}
},
})
================================================
FILE: miniprogram/projects/TRIP1/pages/search/search.json
================================================
{
"usingComponents": {
},
"navigationBarTitleText": "搜索"
}
================================================
FILE: miniprogram/projects/TRIP1/pages/search/search.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/pages/search/search.wxss
================================================
@import "../../style/skin.wxss";
@import "../../../../style/project/search_style.wxss";
================================================
FILE: miniprogram/projects/TRIP1/pages/tpls/menu_tpl.wxml
================================================
================================================
FILE: miniprogram/projects/TRIP1/public/project_setting.js
================================================
module.exports = {
PROJECT_COLOR: '#00C176',
NAV_COLOR: '#ffffff',
NAV_BG: '#00C176',
// setup
SETUP_CONTENT_ITEMS: [
{ title: '景区概况', key: 'SETUP_CONTENT_ABOUT' },
{ title: '公交信息', key: 'SETUP_CONTENT_BUS' },
{ title: '停车场', key: 'SETUP_CONTENT_PARKING' },
{ title: '找厕所', key: 'SETUP_CONTENT_TOLIET' },
{ title: '星级旅行社', key: 'SETUP_CONTENT_LXS' },
{ title: '星级导游', key: 'SETUP_CONTENT_DY' },
{ title: '一周天气', key: 'SETUP_CONTENT_WEATHER' },
],
// 用户
USER_REG_CHECK: false,
USER_FIELDS: [
{ mark: 'sex', title: '性别', type: 'select', selectOptions: ['男', '女'], must: true },
{ mark: 'area', title: '所在地区', type: 'area' }
],
NEWS_NAME: '内容',
NEWS_CATE: [
{ id: 1, title: '景区动态', style: 'upimg' },
{ id: 2, title: '美食', style: 'leftbig1' },
{ id: 3, title: '特产', style: 'leftbig3' },
],
// ### 预约相关
MEET_NAME: '预约',
MEET_TYPE: [
{ id: 1, title: '景点预约', style: 'leftbig2' },
{ id: 2, title: '停车预约', style: 'leftbig3' }
],
MEET_CAN_NULL_TIME: false, // 是否允许有无时段的日期保存和展示
MEET_JOIN_FIELDS: [
{ mark: 'name', type: 'text', title: '姓名', must: true, max: 30 },
{ mark: 'phone', type: 'mobile', title: '手机', must: true }
],
ALBUM_NAME: '攻略',
ALBUM_CATE: [
{ id: 1, title: '线路' },
{ id: 2, title: '吃喝' },
{ id: 3, title: '住宿' },
{ id: 4, title: '购物' },
{ id: 5, title: '其他' },
],
ALBUM_FIELDS: [
{ mark: 'cover', title: '封面图片', type: 'image', min: 1, max: 1, must: true },
{ mark: 'desc', title: '简介', type: 'textarea', max: 100, must: true },
{ mark: 'detail', title: '详细介绍', type: 'content', must: true },
],
PRODUCT_NAME: '景点',
PRODUCT_CATE: [
{ id: 1, title: '仙山贡水' },
{ id: 2, title: '伍家台' },
{ id: 3, title: '狮子关' },
{ id: 4, title: '其他景点' }
],
PRODUCT_FIELDS: [
{ mark: 'cover', title: '封面图片', type: 'image', min: 1, max: 1, must: true },
{ mark: 'star', title: '推荐指数(星级)', type: 'select', selectOptions: ['1', '2', '3', '4', '5'], def: 1, must: true },
{ mark: 'time', title: '开放时间', type: 'textarea', must: false },
{ mark: 'address', title: '地址', type: 'textarea', must: false },
{ mark: 'desc', title: '简介', type: 'textarea', max: 10000, must: true },
{ mark: 'traffic', title: '交通攻略', type: 'textarea', max: 500, must: false },
{ mark: 'ticket', title: '门票攻略', type: 'textarea', max: 500, must: false },
{ mark: 'other', title: '其他信息', type: 'textarea', max: 10000, must: false },
{ mark: 'album', title: '景点图集', type: 'image', min: 1, max: 10, must: true },
],
}
================================================
FILE: miniprogram/projects/TRIP1/style/skin.wxss
================================================
page {
background-color: #f8f8f8;
--projectColor: #00C176;
--projectColorLight: #FEBA00;
--projectCompareColor: #FF473C;
/*#### 父组件日历颜色定义*/
/* 整体颜色 */
--calendarPageColor: #fff;
/* 加重颜色*/
--calendarMainColor: #00C176 !important;
/* 加重的亮颜色 用于选中日期的数据小圆点 */
--calendarLightColor: #FEBA00;
}
.bg-admin {
background-color: #2499f2;
}
.text-project {
color: var(--projectColor) !important;
}
.bg-project {
background-color: var(--projectColor) !important;
}
.bg-project.light {
color: var(--projectColor) !important;
background-color: var(--projectColorLight) !important;
}
.border-project {
border-color: var(--projectColor) !important;
}
.menu-project {
color: var(--projectColor) !important;
}
.tabs .tab {
font-size: 28rpx !important;
}
.tabs .cur {
color: var(--projectColor) !important;
}
.tabs .tab-menu.cur:after {
background-color: var(--projectColor) !important;
}
.load-project {
color: #8799a3 !important;
}
.padding-project {
padding-left: 25rpx !important;
padding-right: 25rpx !important;
}
.btn-project {
line-height: 80rpx;
height: 80rpx;
color: #fff;
background-color: var(--projectColor);
}
.phone-button {
color: var(--projectColor) !important;
}
.shadow-project {
box-shadow: 6rpx 6rpx 8rpx rgba(216, 220, 223, 0.5);
}
.card-project,
.main .list .item.card-project,
.down .comm-list.menu.card-project,
.article-box .article.card-project,
.text-pic-list-box .item.card-project,
.info-list-box .item.card-project,
.main-content .content.card-project,
.comm-list-box .item.card-project,
.form-box.card-project,
.bar .search-form {
border-radius: 10rpx !important;
}
.up-project {
width: 100%;
padding: 0;
margin: 0;
}
.up-project image {
padding: 0;
width: 100%;
margin: 0;
}
.my-icon-project {
font-weight: bold;
font-size: 34rpx;
}
.comm-list-box {
padding: 20rpx 30rpx 30rpx 30rpx;
}
================================================
FILE: miniprogram/setting/setting.js
================================================
module.exports = {
//### 环境相关
CLOUD_ID: 'init-5go8b8pdc98ea814', //云服务id ,本地测试环境
// #### 版本信息
VER: 'build 2022.08.14',
COMPANY: '联系作者',
// #### 系统参数
IS_SUB: false, //分包模式
IS_DEMO: false, //是否演示版
MOBILE_CHECK: false, //手机号码是否真实性校验
//#################
IMG_UPLOAD_SIZE: 20, //图片上传大小M兆
// #### 缓存相关
CACHE_IS_LIST: true, //列表是否缓存
CACHE_LIST_TIME: 60 * 30, //列表缓存时间秒
}
================================================
FILE: miniprogram/sitemap.json
================================================
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "disallow",
"page": "*"
}]
}
================================================
FILE: miniprogram/style/base/animation.wxss
================================================
/* css 滤镜 控制黑白底色gif的 */
.gif-black{
mix-blend-mode: screen;
}
.gif-white{
mix-blend-mode: multiply;
}
/* Animation css */
[class*=animation-] {
animation-duration: .5s;
animation-timing-function: ease-out;
animation-fill-mode: both
}
.animation-fade {
animation-name: fade;
animation-duration: .8s;
animation-timing-function: linear
}
.animation-scale-up {
animation-name: scale-up
}
.animation-scale-down {
animation-name: scale-down
}
.animation-slide-top {
animation-name: slide-top
}
.animation-slide-bottom {
animation-name: slide-bottom
}
.animation-slide-left {
animation-name: slide-left
}
.animation-slide-right {
animation-name: slide-right
}
.animation-shake {
animation-name: shake
}
.animation-reverse {
animation-direction: reverse
}
@keyframes fade {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@keyframes scale-up {
0% {
opacity: 0;
transform: scale(.2)
}
100% {
opacity: 1;
transform: scale(1)
}
}
@keyframes scale-down {
0% {
opacity: 0;
transform: scale(1.8)
}
100% {
opacity: 1;
transform: scale(1)
}
}
@keyframes slide-top {
0% {
opacity: 0;
transform: translateY(-100%)
}
100% {
opacity: 1;
transform: translateY(0)
}
}
@keyframes slide-bottom {
0% {
opacity: 0;
transform: translateY(100%)
}
100% {
opacity: 1;
transform: translateY(0)
}
}
@keyframes shake {
0%,
100% {
transform: translateX(0)
}
10% {
transform: translateX(-9px)
}
20% {
transform: translateX(8px)
}
30% {
transform: translateX(-7px)
}
40% {
transform: translateX(6px)
}
50% {
transform: translateX(-5px)
}
60% {
transform: translateX(4px)
}
70% {
transform: translateX(-3px)
}
80% {
transform: translateX(2px)
}
90% {
transform: translateX(-1px)
}
}
@keyframes slide-left {
0% {
opacity: 0;
transform: translateX(-100%)
}
100% {
opacity: 1;
transform: translateX(0)
}
}
@keyframes slide-right {
0% {
opacity: 0;
transform: translateX(100%)
}
100% {
opacity: 1;
transform: translateX(0)
}
}
================================================
FILE: miniprogram/style/base/avatar.wxss
================================================
/* ==================
头像
==================== */
.avatar {
font-variant: small-caps;
margin: 0;
padding: 0;
display: inline-flex;
text-align: center;
justify-content: center;
align-items: center;
color: var(--white);
white-space: nowrap;
position: relative;
width: 64rpx;
height: 64rpx;
background-size: cover;
background-position: center;
vertical-align: middle;
font-size: 1.5em;
}
.avatar:not([class*="bg-"]) {
background-color: #ccc;
}
.avatar.xs {
width: 32rpx;
height: 32rpx;
font-size: 1em;
}
.avatar.small {
width: 48rpx;
height: 48rpx;
font-size: 1em;
}
.avatar.large {
width: 96rpx;
height: 96rpx;
font-size: 2em;
}
.avatar.xl {
width: 128rpx;
height: 128rpx;
font-size: 2.5em;
}
.avatar .avatar-text {
font-size: 0.4em;
}
.avatar-group {
direction: rtl;
unicode-bidi: bidi-override;
padding: 0 10rpx 0 40rpx;
display: inline-block;
}
.avatar-group .avatar {
margin-left: -30rpx;
border: 4rpx solid var(--ghostWhite);
vertical-align: middle;
}
.avatar-group .avatar.small {
margin-left: -20rpx;
border: 1rpx solid var(--ghostWhite);
}
================================================
FILE: miniprogram/style/base/background.wxss
================================================
/* ==================
背景
==================== */
.bg-red {
background-color: var(--red)!important;
color: var(--white)!important;
}
.bg-orange {
background-color: var(--orange)!important;
color: var(--white)!important;
}
.bg-yellow {
background-color: var(--yellow)!important;
color: var(--black)!important;
}
.bg-olive {
background-color: var(--olive)!important;
color: var(--white)!important;
}
.bg-green {
background-color: var(--green)!important;
color: var(--white)!important;
}
.bg-darkgreen {
background-color: var(--darkgreen)!important;
color: var(--white)!important;
}
.bg-cyan {
background-color: var(--cyan)!important;
color: var(--white)!important;
}
.bg-blue {
background-color: var(--blue)!important;
color: var(--white)!important;
}
.bg-purple {
background-color: var(--purple)!important;
color: var(--white)!important;
}
.bg-mauve {
background-color: var(--mauve)!important;
color: var(--white)!important;
}
.bg-pink {
background-color: var(--pink)!important;
color: var(--white)!important;
}
.bg-brown {
background-color: var(--brown)!important;
color: var(--white)!important;
}
.bg-grey {
background-color: var(--grey)!important;
color: var(--white)!important;
}
.bg-gray {
background-color: #f0f0f0!important;
color: var(--black)!important;
}
.bg-black {
background-color: var(--black)!important;
color: var(--white)!important;
}
.bg-white {
background-color: var(--white)!important;
color: var(--darkGray)!important;
}
.bg-shadeTop {
background-image: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 0.01))!important;
color: var(--white)!important;
}
.bg-shadeBottom {
background-image: linear-gradient(rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 1))!important;
color: var(--white)!important;
}
.bg-red.light {
color: var(--red)!important;
background-color: var(--redLight)!important;
}
.bg-orange.light {
color: var(--orange)!important;
background-color: var(--orangeLight)!important;
}
.bg-yellow.light {
color: var(--yellow)!important;
background-color: var(--yellowLight)!important;
}
.bg-olive.light {
color: var(--olive)!important;
background-color: var(--oliveLight)!important;
}
.bg-green.light {
color: var(--green)!important;
background-color: var(--greenLight)!important;
}
.bg-cyan.light {
color: var(--cyan)!important;
background-color: var(--cyanLight)!important;
}
.bg-blue.light {
color: var(--blue)!important;
background-color: var(--blueLight)!important;
}
.bg-purple.light {
color: var(--purple)!important;
background-color: var(--purpleLight)!important;
}
.bg-mauve.light {
color: var(--mauve)!important;
background-color: var(--mauveLight)!important;
}
.bg-pink.light {
color: var(--pink)!important;
background-color: var(--pinkLight)!important;
}
.bg-brown.light {
color: var(--brown)!important;
background-color: var(--brownLight)!important;
}
.bg-grey.light {
color: var(--grey)!important;
background-color: var(--greyLight)!important;
}
.bg-gradual-red {
background-image: var(--gradualRed)!important;
color: var(--white)!important;
}
.bg-gradual-orange {
background-image: var(--gradualOrange)!important;
color: var(--white)!important;
}
.bg-gradual-green {
background-image: var(--gradualGreen)!important;
color: var(--white)!important;
}
.bg-gradual-purple {
background-image: var(--gradualPurple)!important;
color: var(--white)!important;
}
.bg-gradual-pink {
background-image: var(--gradualPink)!important;
color: var(--white)!important;
}
.bg-gradual-blue {
background-image: var(--gradualBlue)!important;
color: var(--white)!important;
}
================================================
FILE: miniprogram/style/base/bar.wxss
================================================
/* ==================
操作条
==================== */
.bar {
display: flex;
position: relative;
align-items: center;
min-height: 100rpx;
justify-content: space-between;
}
.bar .action {
display: flex;
align-items: center;
height: 100%;
justify-content: center;
max-width: 100%;
}
.bar .content {
position: absolute;
text-align: center;
width: calc(100% - 340rpx);
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
height: 60rpx;
font-size: 32rpx;
line-height: 60rpx;
cursor: none;
pointer-events: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.bar.ios .content {
bottom: 7px;
height: 30px;
font-size: 32rpx;
line-height: 30px;
}
.bar .action:first-child {
margin-left: 30rpx;
font-size: 30rpx;
}
.bar .action:first-child>text[class*="icon-"] {
margin-left: -0.3em;
margin-right: 0.3em;
}
.bar .action:last-child {
margin-right: 30rpx;
}
.bar .action>text[class*="icon-"],
.bar .action>view[class*="icon-"] {
font-size: 36rpx;
}
.bar .action>text[class*="icon-"]+text[class*="icon-"] {
margin-left: 0.5em;
}
.bar .content {
position: absolute;
text-align: center;
width: calc(100% - 340rpx);
left: 0;
right: 0;
bottom: 0;
top: 0;
margin: auto;
height: 60rpx;
font-size: 32rpx;
line-height: 60rpx;
cursor: none;
pointer-events: none;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.bar.ios .content {
bottom: 7px;
height: 30px;
font-size: 32rpx;
line-height: 30px;
}
.bar.btn-group {
justify-content: space-around;
}
.bar.btn-group button {
padding: 20rpx 32rpx;
}
.bar.btn-group button {
flex: 1;
margin: 0 20rpx;
max-width: 50%;
}
.bar .search-form {
background-color: #f5f5f5;
line-height: 64rpx;
height: 64rpx;
font-size: 24rpx;
color: var(--black);
flex: 1;
display: flex;
align-items: center;
margin: 0 30rpx;
}
.bar .search-form+.action {
margin-right: 30rpx;
}
.bar .search-form input {
flex: 1;
padding-right: 30rpx;
height: 64rpx;
line-height: 64rpx;
font-size: 26rpx;
background-color: transparent;
}
.bar .search-form [class*="icon-"] {
margin: 0 0.5em 0 0.8em;
}
.bar .search-form [class*="icon-"]::before {
top: 0rpx;
}
.bar.fixed,
.nav.fixed {
position: fixed;
width: 100%;
top: 0;
z-index: 1024;
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.1);
}
.bar.foot {
position: fixed;
width: 100%;
bottom: 0;
z-index: 1024;
box-shadow: 0 -1rpx 6rpx rgba(0, 0, 0, 0.1);
}
.bar.tabbar {
padding: 0;
height: calc(100rpx + constant(safe-area-inset-bottom) / 2);
height: calc(100rpx + env(safe-area-inset-bottom) / 2);
padding-bottom: calc(constant(safe-area-inset-bottom) / 2);
padding-bottom: calc(env(safe-area-inset-bottom) / 2);
}
.tabbar-height {
min-height: 100rpx;
height: calc(100rpx + constant(safe-area-inset-bottom) / 2);
height: calc(100rpx + env(safe-area-inset-bottom) / 2);
}
.bar.tabbar.shadow {
box-shadow: 0 -1rpx 6rpx rgba(0, 0, 0, 0.1);
}
.bar.tabbar .action {
font-size: 22rpx;
position: relative;
flex: 1;
text-align: center;
padding: 0;
display: block;
height: auto;
line-height: 1;
margin: 0;
overflow: initial;
}
.bar.tabbar.shop .action {
width: 140rpx;
flex: initial;
}
.bar.tabbar .action.add-action {
position: relative;
z-index: 2;
padding-top: 50rpx;
background-color: inherit;
}
.bar.tabbar .action.add-action [class*="icon-"] {
position: absolute;
width: 70rpx;
z-index: 2;
height: 70rpx;
border-radius: 50%;
line-height: 70rpx;
font-size: 50rpx;
top: -35rpx;
left: 0;
right: 0;
margin: auto;
padding: 0;
}
.bar.tabbar .action.add-action::after {
content: "";
position: absolute;
width: 100rpx;
height: 100rpx;
top: -50rpx;
left: 0;
right: 0;
margin: auto;
box-shadow: 0 -3rpx 8rpx rgba(0, 0, 0, 0.08);
border-radius: 50rpx;
background-color: inherit;
z-index: 0;
}
.bar.tabbar .action.add-action::before {
content: "";
position: absolute;
width: 100rpx;
height: 30rpx;
bottom: 30rpx;
left: 0;
right: 0;
margin: auto;
background-color: inherit;
z-index: 1;
}
.bar.tabbar .btn-group {
flex: 1;
display: flex;
justify-content: space-around;
align-items: center;
padding: 0 10rpx;
}
.bar.tabbar button.action::after {
border: 0;
}
.bar.tabbar .action [class*="icon-"] {
width: 100rpx;
position: relative;
display: block;
height: auto;
margin: 0 auto 10rpx;
text-align: center;
font-size: 40rpx;
}
.bar.tabbar .action .icon-cu-image {
margin: 0 auto;
}
.bar.tabbar .action .icon-cu-image image {
width: 50rpx;
height: 50rpx;
display: inline-block;
}
.bar.tabbar .submit {
align-items: center;
display: flex;
justify-content: center;
text-align: center;
position: relative;
flex: 2;
align-self: stretch;
}
.bar.tabbar .submit:last-child {
flex: 2.6;
}
.bar.tabbar .submit+.submit {
flex: 2;
}
.bar.tabbar.border .action::before {
content: " ";
width: 200%;
height: 200%;
position: absolute;
top: 0;
left: 0;
transform: scale(0.5);
transform-origin: 0 0;
border-right: 1rpx solid rgba(0, 0, 0, 0.1);
z-index: 3;
}
.bar.tabbar.border .action:last-child:before {
display: none;
}
.bar.input {
padding-right: 20rpx;
background-color: var(--white);
}
.bar.input input {
overflow: initial;
line-height: 64rpx;
height: 64rpx;
min-height: 64rpx;
flex: 1;
font-size: 30rpx;
margin: 0 20rpx;
}
.bar.input .action {
margin-left: 20rpx;
}
.bar.input .action [class*="icon-"] {
font-size: 48rpx;
}
.bar.input input+.action {
margin-right: 20rpx;
margin-left: 0rpx;
}
.bar.input .action:first-child [class*="icon-"] {
margin-left: 0rpx;
}
================================================
FILE: miniprogram/style/base/base.wxss
================================================
/* ==================
初始化
==================== */
page {
/* Color 可以自定义相关配色 */
/* var属性兼容性 --> https://www.caniuse.com/#feat=css-variables */
/* 标准色 */
--red: #e54d42;
--orange: #f37b1d;
--yellow: #fbbd08;
--olive: #8dc63f;
--green: #39b54a;
--darkgreen: #0E9489;
--cyan: #1cbbb4;
--blue: #347DFF;
--purple: #6739b6;
--mauve: #9c26b0;
--pink: #e03997;
--brown: #a5673f;
--grey: #8799a3;
--black: #333333;
--darkGray: #666666;
--gray: #aaaaaa;
--ghostWhite: #f1f1f1;
--white: #ffffff;
/* 浅色 */
--redLight: #fadbd9;
--orangeLight: #fde6d2;
--yellowLight: #fef2ce;
--oliveLight: #e8f4d9;
--greenLight: #d7f0db;
--cyanLight: #d2f1f0;
--blueLight: #cce6ff;
--purpleLight: #e1d7f0;
--mauveLight: #ebd4ef;
--pinkLight: #f9d7ea;
--brownLight: #ede1d9;
--greyLight: #e7ebed;
/* 阴影透明色 */
--ShadowSize: 6rpx 6rpx 8rpx;
--redShadow: rgba(204, 69, 59, 0.2);
--orangeShadow: rgba(217, 109, 26, 0.2);
--yellowShadow: rgba(224, 170, 7, 0.2);
--oliveShadow: rgba(124, 173, 55, 0.2);
--greenShadow: rgba(48, 156, 63, 0.2);
--darkgreenShadow: rgba(48, 156, 63, 0.2);
--cyanShadow: rgba(28, 187, 180, 0.2);
--blueShadow: rgba(0, 102, 204, 0.2);
--purpleShadow: rgba(88, 48, 156, 0.2);
--mauveShadow: rgba(133, 33, 150, 0.2);
--pinkShadow: rgba(199, 50, 134, 0.2);
--brownShadow: rgba(140, 88, 53, 0.2);
--greyShadow: rgba(114, 130, 138, 0.2);
--grayShadow: rgba(114, 130, 138, 0.2);
--blackShadow: rgba(26, 26, 26, 0.2);
background-color: var(--ghostWhite);
font-size: 28rpx;
color: var(--black);
font-family: Helvetica Neue, Helvetica, sans-serif;
/*苹果手机安全区域*/
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
view,
scroll-view,
swiper,
button,
input,
textarea,
label,
navigator,
image {
box-sizing: border-box;
}
.hide {
display: none;
}
.round {
border-radius: 5000rpx;
}
.radius {
border-radius: 6rpx;
}
.block {
display: block;
}
/* 多行文本 */
.text-content{
line-height: 1.6;
table-layout:fixed; word-break: break-all; overflow:hidden;
}
================================================
FILE: miniprogram/style/base/border.wxss
================================================
/* ==================
边框 line为伪元素
==================== */
.border-red::after {
border-color: var(--red);
}
.border-orange::after {
border-color: var(--orange);
}
.border-yellow::after {
border-color: var(--yellow);
}
.border-olive::after {
border-color: var(--olive);
}
.border-green::after {
border-color: var(--green);
}
.border-darkgreen::after {
border-color: var(--darkgreen);
}
.border-cyan::after {
border-color: var(--cyan);
}
.border-blue::after {
border-color: var(--blue);
}
.border-purple::after {
border-color: var(--purple);
}
.border-mauve::after {
border-color: var(--mauve);
}
.border-pink::after {
border-color: var(--pink);
}
.border-brown::after {
border-color: var(--brown);
}
.border-grey::after {
border-color: var(--grey);
}
.border-gray::after {
border-color: var(--gray);
}
.border-black::after {
border-color: var(--black);
}
.border-white::after {
border-color: var(--white);
}
/* ==================
边框
==================== */
/* -- 实线 -- */
.solid,
.solid-top,
.solid-right,
.solid-bottom,
.solid-left,
.solid-bold,
.solid-bold-top,
.solid-bold-right,
.solid-bold-bottom,
.solid-bold-left {
position: relative;
}
.solid::after,
.solid-top::after,
.solid-right::after,
.solid-bottom::after,
.solid-left::after,
.solid-bold::after,
.solid-bold-top::after,
.solid-bold-right::after,
.solid-bold-bottom::after,
.solid-bold-left::after {
content: " ";
width: 200%;
height: 200%;
position: absolute;
top: 0;
left: 0;
border-radius: inherit;
transform: scale(0.5);
transform-origin: 0 0;
pointer-events: none;
box-sizing: border-box;
}
.solid::after {
border: 4rpx solid rgba(0, 0, 0, 0.1);
}
.solid-top::after {
border-top: 4rpx solid rgba(0, 0, 0, 0.1);
}
.solid-right::after {
border-right: 4rpx solid rgba(0, 0, 0, 0.1);
}
.solid-bottom::after {
border-bottom: 4rpx solid rgba(0, 0, 0, 0.1);
}
.solid-left::after {
border-left: 4rpx solid rgba(0, 0, 0, 0.1);
}
.solid-bold::after {
border: 8rpx solid #eee;
}
.solid-bold-top::after {
border-top: 8rpx solid #eee;
}
.solid-bold-right::after {
border-right: 8rpx solid #eee;
}
.solid-bold-bottom::after {
border-bottom: 8rpx solid #eee;
}
.solid-bold-left::after {
border-left: 8rpx solid #eee;
}
================================================
FILE: miniprogram/style/base/button.wxss
================================================
/* ==================
按钮
==================== */
.btn {
border: 0rpx;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 0 30rpx;
font-size: 28rpx;
height: 64rpx;
line-height: 1;
text-align: center;
text-decoration: none;
overflow: visible;
margin-left: initial;
transform: translate(0rpx, 0rpx);
margin-right: initial;
border-radius: 12rpx;
}
.btn::after {
display: none;
}
.btn:not([class*="bg-"]) {
background-color: #f0f0f0;
}
.btn[class*="border"] {
background-color: transparent;
}
/* 伪元素边框 */
.btn[class*="border"]::after {
content: " ";
display: block;
width: 200%;
height: 200%;
position: absolute;
top: 0;
left: 0;
border: 1rpx solid currentColor;
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
z-index: 1;
pointer-events: none;
border-radius: 12rpx;
}
.btn[class*="bg-"]::after {
display: none;
}
.btn.small {
padding: 0 20rpx;
font-size: 20rpx;
height: 48rpx;
border-radius: 10rpx;
}
.btn.mid {
padding: 0 20rpx;
font-size: 24rpx;
height: 50rpx;
border-radius: 10rpx;
}
.btn.large {
padding: 0 40rpx;
font-size: 32rpx;
height: 80rpx;
border-radius: 14rpx;
}
.btn.btn-icon.small {
width: 48rpx;
height: 48rpx;
}
.btn.btn-icon {
width: 64rpx;
height: 64rpx;
border-radius: 500rpx;
padding: 0;
}
button.btn-icon.large {
width: 80rpx;
height: 80rpx;
}
.btn.shadow-blur::before {
top: 4rpx;
left: 4rpx;
filter: blur(6rpx);
opacity: 0.6;
}
.btn.button-hover {
opacity: .9;
transform: scale(0.95, 0.95);
position: relative;
top: 3rpx;
left: 3rpx;
box-shadow: 0px 0px 8px rgba(0, 0, 0, .1) inset;
}
.btn[disabled] {
opacity: 0.6;
color: var(--white);
}
.btn.round {
border-radius: 5000rpx;
}
.btn.round[class*="border"]::after {
border-radius: 1000rpx;
}
.btn.border-bold::after {
border: 6rpx solid currentColor;
}
================================================
FILE: miniprogram/style/base/comm.wxss
================================================
@import "base.wxss";
@import "avatar.wxss";
@import "background.wxss";
@import "bar.wxss";
@import "border.wxss";
@import "button.wxss";
@import "image.wxss";
@import "modal.wxss";
@import "nav.wxss";
@import "shadow.wxss";
@import "tag.wxss";
@import "icon.wxss";
@import "form.wxss";
@import "text.wxss";
@import "list.wxss";
@import "load.wxss";
@import "layout.wxss";
@import "animation.wxss";
================================================
FILE: miniprogram/style/base/form.wxss
================================================
/* ==================
表单
==================== */
.form-group {
/*background-color: var(--white);影响圆角*/
padding: 1rpx 30rpx;
display: flex;
align-items: center;
min-height: 100rpx;
justify-content: space-between;
position: relative;
border-bottom: 1rpx solid #eee;
}
.form-group:last-child {
border-bottom: 0rpx solid #eee;
}
.form-group .title {
text-align: justify;
padding-right: 30rpx;
font-size: 30rpx;
position: relative;
height: 60rpx;
line-height: 60rpx;
}
.form-group.form-detail {
display: flex;
justify-content: flex-start;
align-items: flex-start;
}
.form-group.form-detail .title {
margin-top: 16rpx;
display: flex;
flex-direction: column;
}
.form-group.form-detail text {
font-size: 24rpx;
font-weight: normal;
}
.form-group input {
flex: 1;
font-size: 30rpx;
color: #555;
padding-right: 20rpx;
height: 60rpx;
}
.modal-form .form-group input {
height: 70rpx;
text-align: right;
background-color: #f8f8f8;
border-radius: 5rpx;
color: #333;
}
.modal-form .form-group .title {
font-weight: normal;
}
.form-group>text[class*="icon-"] {
font-size: 36rpx;
padding: 0;
box-sizing: border-box;
}
.form-group textarea {
margin: 32rpx 0 30rpx;
height: 4.6em;
width: 100%;
line-height: 1.2em;
flex: 1;
font-size: 30rpx;
padding: 0;
text-align: left;
line-height: 1.8;
}
.form-group.arrow::before {
position: absolute;
top: 0;
right: 30rpx;
bottom: 0;
display: block;
margin: auto;
width: 30rpx;
height: 30rpx;
color: var(--grey);
content: "\e6a3";
text-align: center;
font-size: 34rpx;
font-family: "icon";
line-height: 30rpx;
margin-top: auto;
margin-right: auto;
margin-bottom: auto;
margin-left: auto;
}
.form-group .form-text {
padding-right: 40rpx;
flex: 1;
text-align: right;
height: 60rpx;
line-height: 60rpx;
}
.form-group.align-start .title {
height: 1em;
margin-top: 32rpx;
line-height: 1em;
}
.form-group .picker,
.form-group .picker-null {
flex: 1;
padding-right: 40rpx;
overflow: hidden;
position: relative;
}
.form-group .picker .picker,
.form-group .picker-null .picker {
line-height: 100rpx;
font-size: 28rpx;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width: 100%;
text-align: right;
}
.form-group .picker::after,
.form-group .picker-null::after {
font-family: "icon";
display: block;
content: "\e6a3";
position: absolute;
font-size: 34rpx;
color: var(--grey);
line-height: 100rpx;
width: 60rpx;
text-align: center;
top: 0;
bottom: 0;
right: -20rpx;
margin: auto;
}
/* ==================
开关
==================== */
switch,
checkbox,
radio {
position: relative;
}
switch::after,
switch::before {
font-family: "icon";
content: "\e645";
position: absolute;
color: var(--white) !important;
top: 0%;
left: 0rpx;
font-size: 26rpx;
line-height: 26px;
width: 50%;
text-align: center;
pointer-events: none;
transform: scale(0, 0);
transition: all 0.3s ease-in-out 0s;
z-index: 9;
bottom: 0;
height: 26px;
margin: auto;
}
switch::before {
content: "\e646";
right: 0;
transform: scale(1, 1);
left: auto;
}
switch[checked]::after,
switch.checked::after {
transform: scale(1, 1);
}
switch[checked]::before,
switch.checked::before {
transform: scale(0, 0);
}
switch[checked]::before {
transform: scale(0, 0);
}
radio::before,
checkbox::before {
font-family: "icon";
content: "\e645";
position: absolute;
color: var(--white) !important;
top: 50%;
margin-top: -8px;
right: 5px;
font-size: 32rpx;
line-height: 16px;
pointer-events: none;
transform: scale(1, 1);
transition: all 0.3s ease-in-out 0s;
z-index: 9;
}
radio .wx-radio-input,
checkbox .wx-checkbox-input {
margin: 0;
width: 24px;
height: 24px;
}
checkbox.round .wx-checkbox-input {
border-radius: 100rpx;
}
switch .wx-switch-input {
border: none;
padding: 0 24px;
width: 48px;
height: 26px;
margin: 0;
border-radius: 100rpx;
}
switch .wx-switch-input:not([class*="bg-"]) {
background: var(--grey) !important;
}
switch .wx-switch-input::after {
margin: auto;
width: 26px;
height: 26px;
border-radius: 100rpx;
left: 0rpx;
top: 0rpx;
bottom: 0rpx;
position: absolute;
transform: scale(0.9, 0.9);
transition: all 0.1s ease-in-out 0s;
}
switch .wx-switch-input.wx-switch-input-checked::after {
margin: auto;
left: 22px;
box-shadow: none;
transform: scale(0.9, 0.9);
}
radio-group {
display: inline-block;
}
switch.radius .wx-switch-input::after,
switch.radius .wx-switch-input,
switch.radius .wx-switch-input::before {
border-radius: 10rpx;
}
switch .wx-switch-input::before,
radio.radio::before,
checkbox .wx-checkbox-input::before,
radio .wx-radio-input::before,
radio.radio::before {
display: none;
}
radio.radio[checked]::after {
content: "";
background-color: transparent;
display: block;
position: absolute;
width: 8px;
height: 8px;
z-index: 999;
top: 0rpx;
left: 0rpx;
right: 0;
bottom: 0;
margin: auto;
border-radius: 200rpx;
border: 8px solid var(--white) !important;
}
.switch-sex::after {
content: "\e71c";
}
.switch-sex::before {
content: "\e71a";
}
.switch-sex .wx-switch-input {
background: var(--red) !important;
border-color: var(--red) !important;
}
.switch-sex[checked] .wx-switch-input {
background: var(--blue) !important;
border-color: var(--blue) !important;
}
switch.red[checked] .wx-switch-input,
checkbox.red[checked] .wx-checkbox-input,
radio.red[checked] .wx-radio-input {
border-color: var(--red) !important;
}
switch.orange[checked] .wx-switch-input,
checkbox.orange[checked] .wx-checkbox-input,
radio.orange[checked] .wx-radio-input {
border-color: var(--orange) !important;
}
switch.yellow[checked] .wx-switch-input,
checkbox.yellow[checked] .wx-checkbox-input,
radio.yellow[checked] .wx-radio-input {
border-color: var(--yellow) !important;
}
switch.olive[checked] .wx-switch-input,
checkbox.olive[checked] .wx-checkbox-input,
radio.olive[checked] .wx-radio-input {
border-color: var(--olive) !important;
}
switch.green[checked] .wx-switch-input,
checkbox.green[checked] .wx-checkbox-input,
checkbox[checked] .wx-checkbox-input,
radio.green[checked] .wx-radio-input {
border-color: var(--green) !important;
}
switch.cyan[checked] .wx-switch-input,
checkbox.cyan[checked] .wx-checkbox-input,
radio.cyan[checked] .wx-radio-input {
border-color: var(--cyan) !important;
}
switch.blue[checked] .wx-switch-input,
checkbox.blue[checked] .wx-checkbox-input,
radio.blue[checked] .wx-radio-input {
border-color: var(--blue) !important;
}
switch.purple[checked] .wx-switch-input,
checkbox.purple[checked] .wx-checkbox-input,
radio.purple[checked] .wx-radio-input {
border-color: var(--purple) !important;
}
switch.mauve[checked] .wx-switch-input,
checkbox.mauve[checked] .wx-checkbox-input,
radio.mauve[checked] .wx-radio-input {
border-color: var(--mauve) !important;
}
switch.pink[checked] .wx-switch-input,
checkbox.pink[checked] .wx-checkbox-input,
radio.pink[checked] .wx-radio-input {
border-color: var(--pink) !important;
}
switch.brown[checked] .wx-switch-input,
checkbox.brown[checked] .wx-checkbox-input,
radio.brown[checked] .wx-radio-input {
border-color: var(--brown) !important;
}
switch.grey[checked] .wx-switch-input,
checkbox.grey[checked] .wx-checkbox-input,
radio.grey[checked] .wx-radio-input {
border-color: var(--grey) !important;
}
switch.gray[checked] .wx-switch-input,
checkbox.gray[checked] .wx-checkbox-input,
radio.gray[checked] .wx-radio-input {
border-color: var(--grey) !important;
}
switch.black[checked] .wx-switch-input,
checkbox.black[checked] .wx-checkbox-input,
radio.black[checked] .wx-radio-input {
border-color: var(--black) !important;
}
switch.white[checked] .wx-switch-input,
checkbox.white[checked] .wx-checkbox-input,
radio.white[checked] .wx-radio-input {
border-color: var(--white) !important;
}
switch.red[checked] .wx-switch-input.wx-switch-input-checked,
checkbox.red[checked] .wx-checkbox-input,
radio.red[checked] .wx-radio-input {
background-color: var(--red) !important;
color: var(--white) !important;
}
switch.orange[checked] .wx-switch-input,
checkbox.orange[checked] .wx-checkbox-input,
radio.orange[checked] .wx-radio-input {
background-color: var(--orange) !important;
color: var(--white) !important;
}
switch.yellow[checked] .wx-switch-input,
checkbox.yellow[checked] .wx-checkbox-input,
radio.yellow[checked] .wx-radio-input {
background-color: var(--yellow) !important;
color: var(--black) !important;
}
switch.olive[checked] .wx-switch-input,
checkbox.olive[checked] .wx-checkbox-input,
radio.olive[checked] .wx-radio-input {
background-color: var(--olive) !important;
color: var(--white) !important;
}
switch.green[checked] .wx-switch-input,
switch[checked] .wx-switch-input,
checkbox.green[checked] .wx-checkbox-input,
checkbox[checked] .wx-checkbox-input,
radio.green[checked] .wx-radio-input,
radio[checked] .wx-radio-input {
background-color: var(--green) !important;
color: var(--white) !important;
}
switch.cyan[checked] .wx-switch-input,
checkbox.cyan[checked] .wx-checkbox-input,
radio.cyan[checked] .wx-radio-input {
background-color: var(--cyan) !important;
color: var(--white) !important;
}
switch.blue[checked] .wx-switch-input,
checkbox.blue[checked] .wx-checkbox-input,
radio.blue[checked] .wx-radio-input {
background-color: var(--blue) !important;
color: var(--white) !important;
}
switch.purple[checked] .wx-switch-input,
checkbox.purple[checked] .wx-checkbox-input,
radio.purple[checked] .wx-radio-input {
background-color: var(--purple) !important;
color: var(--white) !important;
}
switch.mauve[checked] .wx-switch-input,
checkbox.mauve[checked] .wx-checkbox-input,
radio.mauve[checked] .wx-radio-input {
background-color: var(--mauve) !important;
color: var(--white) !important;
}
switch.pink[checked] .wx-switch-input,
checkbox.pink[checked] .wx-checkbox-input,
radio.pink[checked] .wx-radio-input {
background-color: var(--pink) !important;
color: var(--white) !important;
}
switch.brown[checked] .wx-switch-input,
checkbox.brown[checked] .wx-checkbox-input,
radio.brown[checked] .wx-radio-input {
background-color: var(--brown) !important;
color: var(--white) !important;
}
switch.grey[checked] .wx-switch-input,
checkbox.grey[checked] .wx-checkbox-input,
radio.grey[checked] .wx-radio-input {
background-color: var(--grey) !important;
color: var(--white) !important;
}
switch.gray[checked] .wx-switch-input,
checkbox.gray[checked] .wx-checkbox-input,
radio.gray[checked] .wx-radio-input {
background-color: #f0f0f0 !important;
color: var(--black) !important;
}
switch.black[checked] .wx-switch-input,
checkbox.black[checked] .wx-checkbox-input,
radio.black[checked] .wx-radio-input {
background-color: var(--black) !important;
color: var(--white) !important;
}
switch.white[checked] .wx-switch-input,
checkbox.white[checked] .wx-checkbox-input,
radio.white[checked] .wx-radio-input {
background-color: var(--white) !important;
color: var(--black) !important;
}
.form-group .upload-img {
display: flex;
flex-wrap: wrap;
overflow: hidden;
flex: 1;
}
.form-group .upload-img>view {
width: 25%;
padding-bottom: calc((100% - 60rpx)/4);
height: 0;
width: calc((100% - 60rpx)/4);
margin-right: 12rpx;
margin-bottom: 20rpx;
border-radius: 6rpx;
position: relative;
overflow: hidden;
}
.form-group .upload-img .bg-img {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.form-group .upload-img .bg-img image {
width: 100%;
height: 100%;
position: absolute;
}
.form-group .upload-img>view>text[class*="icon-"] {
font-size: 52rpx;
position: absolute;
color: var(--grey);
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.form-group .upload-img .tag {
position: absolute;
right: 0;
top: 0;
border-bottom-left-radius: 6rpx;
padding: 6rpx 12rpx;
height: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.form-box .hint-desc {
width: 100%;
color: #999;
padding: 0rpx 24rpx 12rpx 24rpx;
}
.form-box .hint-desc.error {
color: #e54d42;
font-size: 24rpx;
}
.form-box .hint-desc.error::before {
font-family: "icon";
font-size: 24rpx !important;
display: inline-block;
content: "\e659";
margin-right: 2rpx;
}
.form-box .hint-desc.error::before {
font-size: 32rpx;
}
.phc {
color: #999;
}
================================================
FILE: miniprogram/style/base/icon.wxss
================================================
@keyframes icon-spin {
0% {
-webkit-transform: rotate(0);
transform: rotate(0);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
.iconfont-spin {
-webkit-animation: icon-spin 2s infinite linear;
animation: icon-spin 2s infinite linear;
display: inline-block;
}
.iconfont-pulse {
-webkit-animation: icon-spin 1s infinite steps(8);
animation: icon-spin 1s infinite steps(8);
display: inline-block;
}
[class*="icon-"] {
font-family: "icon";
font-size: inherit;
font-style: normal;
}
@font-face {
font-family: "icon";
/* IE9*/
src:
url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff')
}
.icon-appreciate:before {
content: "\e644";
}
.icon-check:before {
content: "\e645";
}
.icon-close:before {
content: "\e646";
}
.icon-edit:before {
content: "\e649";
}
.icon-emoji:before {
content: "\e64a";
}
.icon-favorfill:before {
content: "\e64b";
}
.icon-favor:before {
content: "\e64c";
}
.icon-loading:before {
content: "\e64f";
}
.icon-locationfill:before {
content: "\e650";
}
.icon-location:before {
content: "\e651";
}
.icon-phone:before {
content: "\e652";
}
.icon-roundcheckfill:before {
content: "\e656";
}
.icon-roundcheck:before {
content: "\e657";
}
.icon-roundclosefill:before {
content: "\e658";
}
.icon-roundclose:before {
content: "\e659";
}
.icon-roundrightfill:before {
content: "\e65a";
}
.icon-roundright:before {
content: "\e65b";
}
.icon-search:before {
content: "\e65c";
}
.icon-taxi:before {
content: "\e65d";
}
.icon-timefill:before {
content: "\e65e";
}
.icon-time:before {
content: "\e65f";
}
.icon-unfold:before {
content: "\e661";
}
.icon-warnfill:before {
content: "\e662";
}
.icon-warn:before {
content: "\e663";
}
.icon-camerafill:before {
content: "\e664";
}
.icon-camera:before {
content: "\e665";
}
.icon-commentfill:before {
content: "\e666";
}
.icon-comment:before {
content: "\e667";
}
.icon-likefill:before {
content: "\e668";
}
.icon-like:before {
content: "\e669";
}
.icon-notificationfill:before {
content: "\e66a";
}
.icon-notification:before {
content: "\e66b";
}
.icon-order:before {
content: "\e66c";
}
.icon-samefill:before {
content: "\e66d";
}
.icon-same:before {
content: "\e66e";
}
.icon-deliver:before {
content: "\e671";
}
.icon-evaluate:before {
content: "\e672";
}
.icon-pay:before {
content: "\e673";
}
.icon-send:before {
content: "\e675";
}
.icon-shop:before {
content: "\e676";
}
.icon-ticket:before {
content: "\e677";
}
.icon-back:before {
content: "\e679";
}
.icon-cascades:before {
content: "\e67c";
}
.icon-discover:before {
content: "\e67e";
}
.icon-list:before {
content: "\e682";
}
.icon-more:before {
content: "\e684";
}
.icon-scan:before {
content: "\e689";
}
.icon-settings:before {
content: "\e68a";
}
.icon-questionfill:before {
content: "\e690";
}
.icon-question:before {
content: "\e691";
}
.icon-shopfill:before {
content: "\e697";
}
.icon-form:before {
content: "\e699";
}
.icon-pic:before {
content: "\e69b";
}
.icon-filter:before {
content: "\e69c";
}
.icon-footprint:before {
content: "\e69d";
}
.icon-top:before {
content: "\e69e";
}
.icon-pulldown:before {
content: "\e69f";
}
.icon-pullup:before {
content: "\e6a0";
}
.icon-right:before {
content: "\e6a3";
}
.icon-refresh:before {
content: "\e6a4";
}
.icon-moreandroid:before {
content: "\e6a5";
}
.icon-deletefill:before {
content: "\e6a6";
}
.icon-refund:before {
content: "\e6ac";
}
.icon-cart:before {
content: "\e6af";
}
.icon-qrcode:before {
content: "\e6b0";
}
.icon-remind:before {
content: "\e6b2";
}
.icon-delete:before {
content: "\e6b4";
}
.icon-profile:before {
content: "\e6b7";
}
.icon-home:before {
content: "\e6b8";
}
.icon-cartfill:before {
content: "\e6b9";
}
.icon-discoverfill:before {
content: "\e6ba";
}
.icon-homefill:before {
content: "\e6bb";
}
.icon-message:before {
content: "\e6bc";
}
.icon-addressbook:before {
content: "\e6bd";
}
.icon-link:before {
content: "\e6bf";
}
.icon-lock:before {
content: "\e6c0";
}
.icon-unlock:before {
content: "\e6c2";
}
.icon-vip:before {
content: "\e6c3";
}
.icon-weibo:before {
content: "\e6c4";
}
.icon-activity:before {
content: "\e6c5";
}
.icon-friendaddfill:before {
content: "\e6c9";
}
.icon-friendadd:before {
content: "\e6ca";
}
.icon-friendfamous:before {
content: "\e6cb";
}
.icon-friend:before {
content: "\e6cc";
}
.icon-goods:before {
content: "\e6cd";
}
.icon-selection:before {
content: "\e6ce";
}
.icon-explore:before {
content: "\e6d2";
}
.icon-present:before {
content: "\e6d3";
}
.icon-squarecheckfill:before {
content: "\e6d4";
}
.icon-square:before {
content: "\e6d5";
}
.icon-squarecheck:before {
content: "\e6d6";
}
.icon-round:before {
content: "\e6d7";
}
.icon-roundaddfill:before {
content: "\e6d8";
}
.icon-roundadd:before {
content: "\e6d9";
}
.icon-add:before {
content: "\e6da";
}
.icon-notificationforbidfill:before {
content: "\e6db";
}
.icon-explorefill:before {
content: "\e6dd";
}
.icon-fold:before {
content: "\e6de";
}
.icon-game:before {
content: "\e6df";
}
.icon-redpacket:before {
content: "\e6e0";
}
.icon-selectionfill:before {
content: "\e6e1";
}
.icon-similar:before {
content: "\e6e2";
}
.icon-appreciatefill:before {
content: "\e6e3";
}
.icon-infofill:before {
content: "\e6e4";
}
.icon-info:before {
content: "\e6e5";
}
.icon-forwardfill:before {
content: "\e6ea";
}
.icon-forward:before {
content: "\e6eb";
}
.icon-rechargefill:before {
content: "\e6ec";
}
.icon-recharge:before {
content: "\e6ed";
}
.icon-vipcard:before {
content: "\e6ee";
}
.icon-voice:before {
content: "\e6ef";
}
.icon-voicefill:before {
content: "\e6f0";
}
.icon-friendfavor:before {
content: "\e6f1";
}
.icon-wifi:before {
content: "\e6f2";
}
.icon-share:before {
content: "\e6f3";
}
.icon-wefill:before {
content: "\e6f4";
}
.icon-we:before {
content: "\e6f5";
}
.icon-lightauto:before {
content: "\e6f6";
}
.icon-lightforbid:before {
content: "\e6f7";
}
.icon-lightfill:before {
content: "\e6f8";
}
.icon-camerarotate:before {
content: "\e6f9";
}
.icon-light:before {
content: "\e6fa";
}
.icon-barcode:before {
content: "\e6fb";
}
.icon-flashlightclose:before {
content: "\e6fc";
}
.icon-flashlightopen:before {
content: "\e6fd";
}
.icon-searchlist:before {
content: "\e6fe";
}
.icon-service:before {
content: "\e6ff";
}
.icon-sort:before {
content: "\e700";
}
.icon-down:before {
content: "\e703";
}
.icon-mobile:before {
content: "\e704";
}
.icon-mobilefill:before {
content: "\e705";
}
.icon-copy:before {
content: "\e706";
}
.icon-countdownfill:before {
content: "\e707";
}
.icon-countdown:before {
content: "\e708";
}
.icon-noticefill:before {
content: "\e709";
}
.icon-notice:before {
content: "\e70a";
}
.icon-upstagefill:before {
content: "\e70e";
}
.icon-upstage:before {
content: "\e70f";
}
.icon-babyfill:before {
content: "\e710";
}
.icon-baby:before {
content: "\e711";
}
.icon-brandfill:before {
content: "\e712";
}
.icon-brand:before {
content: "\e713";
}
.icon-choicenessfill:before {
content: "\e714";
}
.icon-choiceness:before {
content: "\e715";
}
.icon-clothesfill:before {
content: "\e716";
}
.icon-clothes:before {
content: "\e717";
}
.icon-creativefill:before {
content: "\e718";
}
.icon-creative:before {
content: "\e719";
}
.icon-female:before {
content: "\e71a";
}
.icon-keyboard:before {
content: "\e71b";
}
.icon-male:before {
content: "\e71c";
}
.icon-newfill:before {
content: "\e71d";
}
.icon-new:before {
content: "\e71e";
}
.icon-pullleft:before {
content: "\e71f";
}
.icon-pullright:before {
content: "\e720";
}
.icon-rankfill:before {
content: "\e721";
}
.icon-rank:before {
content: "\e722";
}
.icon-bad:before {
content: "\e723";
}
.icon-cameraadd:before {
content: "\e724";
}
.icon-focus:before {
content: "\e725";
}
.icon-friendfill:before {
content: "\e726";
}
.icon-cameraaddfill:before {
content: "\e727";
}
.icon-apps:before {
content: "\e729";
}
.icon-paintfill:before {
content: "\e72a";
}
.icon-paint:before {
content: "\e72b";
}
.icon-picfill:before {
content: "\e72c";
}
.icon-refresharrow:before {
content: "\e72d";
}
.icon-refresharrow.up {
transform: rotateX(180deg);
}
.icon-colorlens:before {
content: "\e6e6";
}
.icon-markfill:before {
content: "\e730";
}
.icon-mark:before {
content: "\e731";
}
.icon-presentfill:before {
content: "\e732";
}
.icon-repeal:before {
content: "\e733";
}
.icon-album:before {
content: "\e734";
}
.icon-peoplefill:before {
content: "\e735";
}
.icon-people:before {
content: "\e736";
}
.icon-servicefill:before {
content: "\e737";
}
.icon-repair:before {
content: "\e738";
}
.icon-file:before {
content: "\e739";
}
.icon-repairfill:before {
content: "\e73a";
}
.icon-taoxiaopu:before {
content: "\e73b";
}
.icon-weixin:before {
content: "\e612";
}
.icon-attentionfill:before {
content: "\e73c";
}
.icon-attention:before {
content: "\e73d";
}
.icon-commandfill:before {
content: "\e73e";
}
.icon-command:before {
content: "\e73f";
}
.icon-communityfill:before {
content: "\e740";
}
.icon-community:before {
content: "\e741";
}
.icon-read:before {
content: "\e742";
}
.icon-calendar:before {
content: "\e74a";
}
.icon-cut:before {
content: "\e74b";
}
.icon-magic:before {
content: "\e74c";
}
.icon-backwardfill:before {
content: "\e74d";
}
.icon-playfill:before {
content: "\e74f";
}
.icon-stop:before {
content: "\e750";
}
.icon-tagfill:before {
content: "\e751";
}
.icon-tag:before {
content: "\e752";
}
.icon-group:before {
content: "\e753";
}
.icon-all:before {
content: "\e755";
}
.icon-backdelete:before {
content: "\e756";
}
.icon-hotfill:before {
content: "\e757";
}
.icon-hot:before {
content: "\e758";
}
.icon-post:before {
content: "\e759";
}
.icon-radiobox:before {
content: "\e75b";
}
.icon-rounddown:before {
content: "\e75c";
}
.icon-upload:before {
content: "\e75d";
}
.icon-writefill:before {
content: "\e760";
}
.icon-write:before {
content: "\e761";
}
.icon-radioboxfill:before {
content: "\e763";
}
.icon-punch:before {
content: "\e764";
}
.icon-shake:before {
content: "\e765";
}
.icon-move:before {
content: "\e768";
}
.icon-safe:before {
content: "\e769";
}
.icon-activityfill:before {
content: "\e775";
}
.icon-crownfill:before {
content: "\e776";
}
.icon-crown:before {
content: "\e777";
}
.icon-goodsfill:before {
content: "\e778";
}
.icon-messagefill:before {
content: "\e779";
}
.icon-profilefill:before {
content: "\e77a";
}
.icon-sound:before {
content: "\e77b";
}
.icon-sponsorfill:before {
content: "\e77c";
}
.icon-sponsor:before {
content: "\e77d";
}
.icon-upblock:before {
content: "\e77e";
}
.icon-weblock:before {
content: "\e77f";
}
.icon-weunblock:before {
content: "\e780";
}
.icon-my:before {
content: "\e78b";
}
.icon-myfill:before {
content: "\e78c";
}
.icon-emojifill:before {
content: "\e78d";
}
.icon-emojiflashfill:before {
content: "\e78e";
}
.icon-flashbuyfill:before {
content: "\e78f";
}
.icon-text:before {
content: "\e791";
}
.icon-goodsfavor:before {
content: "\e794";
}
.icon-musicfill:before {
content: "\e795";
}
.icon-musicforbidfill:before {
content: "\e796";
}
.icon-card:before {
content: "\e624";
}
.icon-triangledownfill:before {
content: "\e79b";
}
.icon-triangleupfill:before {
content: "\e79c";
}
.icon-roundleftfill-copy:before {
content: "\e79e";
}
.icon-font:before {
content: "\e76a";
}
.icon-title:before {
content: "\e82f";
}
.icon-recordfill:before {
content: "\e7a4";
}
.icon-record:before {
content: "\e7a6";
}
.icon-cardboardfill:before {
content: "\e7a9";
}
.icon-cardboard:before {
content: "\e7aa";
}
.icon-formfill:before {
content: "\e7ab";
}
.icon-coin:before {
content: "\e7ac";
}
.icon-cardboardforbid:before {
content: "\e7af";
}
.icon-circlefill:before {
content: "\e7b0";
}
.icon-circle:before {
content: "\e7b1";
}
.icon-attentionforbid:before {
content: "\e7b2";
}
.icon-attentionforbidfill:before {
content: "\e7b3";
}
.icon-attentionfavorfill:before {
content: "\e7b4";
}
.icon-attentionfavor:before {
content: "\e7b5";
}
.icon-titles:before {
content: "\e701";
}
.icon-icloading:before {
content: "\e67a";
}
.icon-full:before {
content: "\e7bc";
}
.icon-mail:before {
content: "\e7bd";
}
.icon-peoplelist:before {
content: "\e7be";
}
.icon-goodsnewfill:before {
content: "\e7bf";
}
.icon-goodsnew:before {
content: "\e7c0";
}
.icon-medalfill:before {
content: "\e7c1";
}
.icon-medal:before {
content: "\e7c2";
}
.icon-newsfill:before {
content: "\e7c3";
}
.icon-newshotfill:before {
content: "\e7c4";
}
.icon-newshot:before {
content: "\e7c5";
}
.icon-news:before {
content: "\e7c6";
}
.icon-videofill:before {
content: "\e7c7";
}
.icon-video:before {
content: "\e7c8";
}
.icon-exit:before {
content: "\e7cb";
}
.icon-skinfill:before {
content: "\e7cc";
}
.icon-skin:before {
content: "\e7cd";
}
.icon-moneybagfill:before {
content: "\e7ce";
}
.icon-usefullfill:before {
content: "\e7cf";
}
.icon-usefull:before {
content: "\e7d0";
}
.icon-moneybag:before {
content: "\e7d1";
}
.icon-redpacket_fill:before {
content: "\e7d3";
}
.icon-subscription:before {
content: "\e7d4";
}
.icon-loading1:before {
content: "\e633";
}
.icon-github:before {
content: "\e692";
}
.icon-global:before {
content: "\e7eb";
}
.icon-settingsfill:before {
content: "\e6ab";
}
.icon-back_android:before {
content: "\e7ed";
}
.icon-expressman:before {
content: "\e7ef";
}
.icon-evaluate_fill:before {
content: "\e7f0";
}
.icon-group_fill:before {
content: "\e7f5";
}
.icon-play_forward_fill:before {
content: "\e7f6";
}
.icon-deliver_fill:before {
content: "\e7f7";
}
.icon-notice_forbid_fill:before {
content: "\e7f8";
}
.icon-fork:before {
content: "\e60c";
}
.icon-pick:before {
content: "\e7fa";
}
.icon-wenzi:before {
content: "\e6a7";
}
.icon-ellipse:before {
content: "\e600";
}
.icon-qr_code:before {
content: "\e61b";
}
.icon-dianhua:before {
content: "\e64d";
}
.icon-icon:before {
content: "\e602";
}
.icon-loading2:before {
content: "\e7f1";
}
.icon-btn:before {
content: "\e601";
}
================================================
FILE: miniprogram/style/base/image.wxss
================================================
/* ==================
图片
==================== */
image {
max-width: 100%;
display: inline-block;
position: relative;
z-index: 0;
}
image.loading::before {
content: "";
background-color: #f5f5f5;
display: block;
position: absolute;
width: 100%;
height: 100%;
z-index: -2;
}
image.loading::after {
content: "\e7f1";
font-family: "icon";
position: absolute;
top: 0;
left: 0;
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
right: 0;
bottom: 0;
z-index: -1;
font-size: 32rpx;
margin: auto;
color: #ccc;
-webkit-animation: icon-spin 2s infinite linear;
animation: icon-spin 2s infinite linear;
display: block;
}
================================================
FILE: miniprogram/style/base/layout.wxss
================================================
/* ==================
布局
==================== */
/* -- flex弹性布局 -- */
.flex {
display: flex;
}
.flex-row {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.flex-column {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.basis-xs {
flex-basis: 20%;
}
.basis-sm {
flex-basis: 40%;
}
.basis-df {
flex-basis: 50%;
}
.basis-lg {
flex-basis: 60%;
}
.basis-xl {
flex-basis: 80%;
}
.flex-sub {
flex: 1;
}
.flex-twice {
flex: 2;
}
.flex-treble {
flex: 3;
}
.flex-direction {
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.align-start {
align-items: flex-start;
}
.align-end {
align-items: flex-end;
}
.align-center {
align-items: center;
}
.align-stretch {
align-items: stretch;
}
.self-start {
align-self: flex-start;
}
.self-center {
align-self: flex-center;
}
.self-end {
align-self: flex-end;
}
.self-stretch {
align-self: stretch;
}
.align-stretch {
align-items: stretch;
}
.justify-start {
justify-content: flex-start;
}
.justify-end {
justify-content: flex-end;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
/* -- 内外边距 -- */
.margin-0 {
margin: 0;
}
.margin-xxs {
margin: 5rpx;
}
.margin-xs {
margin: 10rpx;
}
.margin-s {
margin: 20rpx;
}
.margin {
margin: 30rpx;
}
.margin-lg {
margin: 40rpx;
}
.margin-xl {
margin: 50rpx;
}
.margin-xxl {
margin: 60rpx;
}
.margin-top-xxxs {
margin-top: 5rpx;
}
.margin-top-xxs {
margin-top: 5rpx;
}
.margin-top-xs {
margin-top: 10rpx;
}
.margin-top-s {
margin-top: 20rpx;
}
.margin-top {
margin-top: 30rpx;
}
.margin-top-l {
margin-top: 40rpx;
}
.margin-top-xl {
margin-top: 50rpx;
}
.margin-top-xxl {
margin-top: 60rpx;
}
.margin-right-xxxs {
margin-right: 3rpx;
}
.margin-right-xxs {
margin-right: 5rpx;
}
.margin-right-xs {
margin-right: 10rpx;
}
.margin-right-s {
margin-right: 20rpx;
}
.margin-right {
margin-right: 30rpx;
}
.margin-right-l {
margin-right: 40rpx;
}
.margin-right-xl {
margin-right: 50rpx;
}
.margin-right-xxl {
margin-right: 60rpx;
}
.margin-bottom-xxs {
margin-bottom: 5rpx;
}
.margin-bottom-xxxs {
margin-bottom: 3rpx;
}
.margin-bottom-xs {
margin-bottom: 10rpx;
}
.margin-bottom-s {
margin-bottom: 20rpx;
}
.margin-bottom {
margin-bottom: 30rpx;
}
.margin-bottom-l {
margin-bottom: 40rpx;
}
.margin-bottom-xl {
margin-bottom: 50rpx;
}
.margin-bottom-xxl {
margin-bottom: 60rpx;
}
.margin-bottom-xxxl {
margin-bottom: 70rpx;
}
.safe-bottom {
padding-bottom: constant(safe-area-inset-bottom) !important;
padding-bottom: env(safe-area-inset-bottom) !important;
}
.margin-left-xxxs {
margin-left: 3rpx;
}
.margin-left-xxs {
margin-left: 5rpx;
}
.margin-left-xs {
margin-left: 10rpx;
}
.margin-left-s {
margin-left: 20rpx;
}
.margin-left {
margin-left: 30rpx;
}
.margin-left-l {
margin-left: 40rpx;
}
.margin-left-xl {
margin-left: 50rpx;
}
.margin-left-xxl {
margin-left: 60rpx;
}
.padding-0 {
padding: 0;
}
.padding-xs {
padding: 10rpx;
}
.padding-s {
padding: 20rpx;
}
.padding {
padding: 30rpx;
}
.padding-l {
padding: 40rpx;
}
.padding-xl {
padding: 50rpx;
}
.padding-top-xs {
padding-top: 10rpx;
}
.padding-top-s {
padding-top: 20rpx;
}
.padding-top {
padding-top: 30rpx;
}
.padding-top-l {
padding-top: 40rpx;
}
.padding-top-xl {
padding-top: 50rpx;
}
.padding-right-xs {
padding-right: 10rpx;
}
.padding-right-s {
padding-right: 20rpx;
}
.padding-right {
padding-right: 30rpx;
}
.padding-right-l {
padding-right: 40rpx;
}
.padding-right-xl {
padding-right: 50rpx;
}
.padding-bottom-xs {
padding-bottom: 10rpx;
}
.padding-bottom-s {
padding-bottom: 20rpx;
}
.padding-bottom {
padding-bottom: 30rpx;
}
.padding-bottom-l {
padding-bottom: 40rpx;
}
.padding-bottom-xl {
padding-bottom: 50rpx;
}
.padding-left-xs {
padding-left: 10rpx;
}
.padding-left-s {
padding-left: 20rpx;
}
.padding-left {
padding-left: 30rpx;
}
.padding-left-l {
padding-left: 40rpx;
}
.padding-left-xl {
padding-left: 50rpx;
}
.padding-lr-xs {
padding-left: 10rpx;
padding-right: 10rpx;
}
.padding-lr-s {
padding-left: 20rpx;
padding-right: 20rpx;
}
.padding-lr {
padding-left: 30rpx;
padding-right: 30rpx;
}
.padding-lr-l {
padding-left: 40rpx;
padding-right: 40rpx;
}
.padding-lr-xl {
padding-left: 50rpx;
padding-right: 50rpx;
}
/* grid布局 */
.grid {
display: flex;
flex-wrap: wrap;
}
.grid.grid-square {
overflow: hidden;
}
.grid.grid-square .cu-tag {
position: absolute;
right: 0;
top: 0;
border-bottom-left-radius: 6rpx;
padding: 6rpx 12rpx;
height: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.grid.grid-square>view>text[class*="cuIcon-"] {
font-size: 52rpx;
position: absolute;
color: var(--grey);
margin: auto;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.grid.grid-square>view {
margin-right: 20rpx;
margin-bottom: 20rpx;
border-radius: 6rpx;
position: relative;
overflow: hidden;
}
.grid.grid-square>view.bg-img image {
width: 100%;
height: 100%;
position: absolute;
}
.grid.col-1.grid-square>view {
padding-bottom: 100%;
height: 0;
margin-right: 0;
}
.grid.col-2.grid-square>view {
padding-bottom: calc((100% - 20rpx)/2);
height: 0;
width: calc((100% - 20rpx)/2);
}
.grid.col-3.grid-square>view {
padding-bottom: calc((100% - 40rpx)/3);
height: 0;
width: calc((100% - 40rpx)/3);
}
.grid.col-4.grid-square>view {
padding-bottom: calc((100% - 60rpx)/4);
height: 0;
width: calc((100% - 60rpx)/4);
}
.grid.col-5.grid-square>view {
padding-bottom: calc((100% - 80rpx)/5);
height: 0;
width: calc((100% - 80rpx)/5);
}
.grid.col-2.grid-square>view:nth-child(2n),
.grid.col-3.grid-square>view:nth-child(3n),
.grid.col-4.grid-square>view:nth-child(4n),
.grid.col-5.grid-square>view:nth-child(5n) {
margin-right: 0;
}
.grid.col-1>view {
width: 100%;
}
.grid.col-2>view {
width: 50%;
}
.grid.col-3>view {
width: 33.33%;
}
.grid.col-4>view {
width: 25%;
}
.grid.col-5>view {
width: 20%;
}
================================================
FILE: miniprogram/style/base/list.wxss
================================================
/* ==================
列表
==================== */
.grayscale {
filter: grayscale(1);
}
.comm-list+.comm-list {
margin-top: 30rpx
}
.comm-list>.item {
transition: all .6s ease-in-out 0s;
transform: translateX(0rpx)
}
.comm-list>.item.move-cur {
transform: translateX(-260rpx)
}
.comm-list>.item .move {
position: absolute;
right: 0;
display: flex;
width: 260rpx;
height: 100%;
transform: translateX(100%)
}
.comm-list>.item .move view {
display: flex;
flex: 1;
justify-content: center;
align-items: center
}
.comm-list.menu-avatar {
overflow: hidden;
}
.comm-list.menu-avatar>.item {
position: relative;
display: flex;
padding-right: 10rpx;
height: 140rpx;
background-color: var(--white);
justify-content: flex-end;
align-items: center
}
.comm-list.menu-avatar>.item>.avatar {
position: absolute;
left: 30rpx
}
.comm-list.menu-avatar>.item .flex .text-cut {
max-width: 510rpx
}
.comm-list.menu-avatar>.item .content {
position: absolute;
left: 146rpx;
width: calc(100% - 96rpx - 60rpx - 120rpx - 20rpx);
line-height: 1.6em;
}
.comm-list.menu-avatar>.item .content.flex-sub {
width: calc(100% - 96rpx - 60rpx - 20rpx);
}
.comm-list.menu-avatar>.item .content>view:first-child {
font-size: 30rpx;
display: flex;
align-items: center
}
.comm-list.menu-avatar>.item .content .tag.small {
display: inline-block;
margin-left: 10rpx;
height: 28rpx;
font-size: 16rpx;
line-height: 32rpx
}
.comm-list.menu-avatar>.item .action {
width: 100rpx;
text-align: center
}
.comm-list.menu-avatar>.item .action view+view {
margin-top: 10rpx
}
.comm-list.menu-avatar.comment>.item .content {
position: relative;
left: 0;
width: auto;
flex: 1;
}
.comm-list.menu-avatar.comment>.item {
padding: 30rpx 30rpx 30rpx 120rpx;
height: auto
}
.comm-list.menu-avatar.comment .avatar {
align-self: flex-start
}
.comm-list.menu>.item {
position: relative;
display: flex;
padding: 0 30rpx;
min-height: 100rpx;
background-color: var(--white);
justify-content: space-between;
align-items: center
}
.comm-list.menu>.item:last-child:after {
border: none
}
.comm-list.menu>.item:after {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 200%;
height: 200%;
border-bottom: 1rpx solid #ddd;
border-radius: inherit;
content: " ";
transform: scale(.5);
transform-origin: 0 0;
pointer-events: none
}
.comm-list.menu>.item.grayscale {
background-color: #f5f5f5
}
.comm-list.menu>.item.cur {
background-color: #fcf7e9
}
.comm-list.menu>.item.arrow {
padding-right: 90rpx
}
.comm-list.menu>.item.arrow:before {
position: absolute;
top: 0;
right: 30rpx;
bottom: 0;
display: block;
margin: auto;
width: 30rpx;
height: 30rpx;
color: var(--grey);
content: "\e6a3";
text-align: center;
font-size: 34rpx;
font-family: "icon";
line-height: 30rpx
}
.comm-list.menu>.item button.content {
padding: 0;
background-color: transparent;
justify-content: flex-start
}
.comm-list.menu>.item button.content:after {
display: none
}
.comm-list.menu>.item .avatar-group .avatar {
border-color: var(--white)
}
.comm-list.menu>.item .content>view:first-child {
display: flex;
align-items: center
}
.comm-list.menu>.item .content>text[class*=icon] {
display: inline-block;
margin-right: 10rpx;
width: 1.6em;
text-align: center
}
.comm-list.menu>.item .content>image {
display: inline-block;
margin-right: 10rpx;
width: 1.6em;
height: 1.6em;
vertical-align: middle
}
.comm-list.menu>.item .content {
font-size: 30rpx;
line-height: 1.6em;
flex: 1
}
.comm-list.menu>.item .content .tag.small {
display: inline-block;
margin-left: 10rpx;
height: 28rpx;
font-size: 16rpx;
line-height: 32rpx
}
.comm-list.menu>.item .action .tag:empty {
right: 10rpx
}
.comm-list.menu {
display: block;
overflow: hidden
}
.comm-list.menu.small-border>.item:after {
left: 30rpx;
width: calc(200% - 120rpx)
}
.comm-list.grid>.item {
position: relative;
display: flex;
padding: 20rpx 0 30rpx;
transition-duration: 0s;
flex-direction: column
}
.comm-list.grid>.item:after {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 200%;
height: 200%;
border-right: 1px solid rgba(0, 0, 0, .1);
border-bottom: 1px solid rgba(0, 0, 0, .1);
border-radius: inherit;
content: " ";
transform: scale(.5);
transform-origin: 0 0;
pointer-events: none
}
.comm-list.grid>.item text {
display: block;
margin-top: 10rpx;
color: #888;
font-size: 26rpx;
line-height: 40rpx
}
.comm-list.grid>.item [class*=icon] {
position: relative;
display: block;
margin-top: 20rpx;
width: 100%;
font-size: 48rpx
}
.comm-list.grid>.item .tag {
right: auto;
left: 50%;
margin-left: 20rpx
}
.comm-list.grid {
background-color: var(--white);
text-align: center
}
.comm-list.grid.no-border>.item {
padding-top: 10rpx;
padding-bottom: 20rpx
}
.comm-list.grid.no-border>.item:after {
border: none
}
.comm-list.grid.no-border {
padding: 20rpx 10rpx
}
.comm-list.grid.col-3>.item:nth-child(3n):after,
.comm-list.grid.col-4>.item:nth-child(4n):after,
.comm-list.grid.col-5>.item:nth-child(5n):after {
border-right-width: 0
}
.comm-list.card-menu {
overflow: hidden;
margin-right: 30rpx;
margin-left: 30rpx;
border-radius: 20rpx
}
================================================
FILE: miniprogram/style/base/load.wxss
================================================
/* ==================
加载
==================== */
.load {
display: block;
line-height: 3em;
text-align: center;
z-index: 99999;
}
.load::before {
font-family: "icon";
display: inline-block;
margin-right: 6rpx;
}
.load.loading::before, .load.loading-none::before {
content: "\e67a";
animation: icon-spin 2s infinite linear;
}
.load.loading::after {
content: "加载中...";
}
.load.over::before {
content: "\e64a";
}
.load.over::after {
content: "没有更多了";
}
.load.error::before {
content: "\e658";
}
/*
.load.error::after {
content: "加载失败";
}*/
.load.notexist::before {
content: "\e658";
}
.load.notexist::after {
content: "数据不存在或者已删除";
}
.load.load-icon::before {
font-size: 32rpx;
}
.load.load-icon::after {
display: none;
}
.load.load-icon.over {
display: none;
}
.load.load-modal {
position: fixed;
top: 0;
right: 0;
bottom: 140rpx;
left: 0;
margin: auto;
width: 260rpx;
height: 260rpx;
background-color: var(--white);
border-radius: 10rpx;
box-shadow: 0 0 0rpx 2000rpx rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
font-size: 28rpx;
z-index: 9999;
line-height: 2.4em;
}
.load.load-modal [class*="icon-"] {
font-size: 60rpx;
}
.load.load-modal image {
width: 70rpx;
height: 70rpx;
}
.load.load-modal::after {
content: "";
position: absolute;
background-color: var(--white);
border-radius: 50%;
width: 200rpx;
height: 200rpx;
font-size: 10px;
border-top: 6rpx solid rgba(0, 0, 0, 0.05);
border-right: 6rpx solid rgba(0, 0, 0, 0.05);
border-bottom: 6rpx solid rgba(0, 0, 0, 0.05);
border-left: 6rpx solid var(--orange);
animation: icon-spin 1s infinite linear;
z-index: -1;
}
.load-progress {
pointer-events: none;
top: 0;
position: fixed;
width: 100%;
left: 0;
z-index: 2000;
}
.load-progress.hide {
display: none;
}
.load-progress .load-progress-bar {
position: relative;
width: 100%;
height: 4rpx;
overflow: hidden;
transition: all 200ms ease 0s;
}
.load-progress .load-progress-spinner {
position: absolute;
top: 10rpx;
right: 10rpx;
z-index: 2000;
display: block;
}
.load-progress .load-progress-spinner::after {
content: "";
display: block;
width: 24rpx;
height: 24rpx;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border: solid 4rpx transparent;
border-top-color: inherit;
border-left-color: inherit;
border-radius: 50%;
-webkit-animation: load-progress-spinner 0.4s linear infinite;
animation: load-progress-spinner 0.4s linear infinite;
}
@-webkit-keyframes load-progress-spinner {
0% {
-webkit-transform: rotate(0);
transform: rotate(0);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes load-progress-spinner {
0% {
-webkit-transform: rotate(0);
transform: rotate(0);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
/* ==================
进度条
==================== */
.progress {
overflow: hidden;
height: 28rpx;
background-color: #ebeef5;
display: inline-flex;
align-items: center;
width: 100%;
}
.progress+view,
.progress+text {
line-height: 1;
}
.progress.xs {
height: 10rpx;
}
.progress.sm {
height: 20rpx;
}
.progress view {
width: 0;
height: 100%;
align-items: center;
display: flex;
justify-items: flex-end;
justify-content: space-around;
font-size: 20rpx;
color: var(--white);
transition: width 0.6s ease;
}
.progress text {
align-items: center;
display: flex;
font-size: 20rpx;
color: var(--black);
text-indent: 10rpx;
}
.progress.text-progress {
padding-right: 60rpx;
}
.progress.striped view {
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-size: 72rpx 72rpx;
}
.progress.active view {
animation: progress-stripes 2s linear infinite;
}
@keyframes progress-stripes {
from {
background-position: 72rpx 0;
}
to {
background-position: 0 0;
}
}
================================================
FILE: miniprogram/style/base/modal.wxss
================================================
/* ==================
模态窗口
==================== */
.modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1110;
opacity: 0;
outline: 0;
text-align: center;
-ms-transform: scale(1.185);
transform: scale(1.185);
backface-visibility: hidden;
perspective: 2000rpx;
background: rgba(0, 0, 0, 0.6);
transition: all 0.3s ease-in-out 0s;
pointer-events: none;
}
.modal::before {
content: "\200B";
display: inline-block;
height: 100%;
vertical-align: middle;
}
.modal.show {
opacity: 1;
transition-duration: 0.3s;
-ms-transform: scale(1);
transform: scale(1);
overflow-x: hidden;
overflow-y: auto;
pointer-events: auto;
}
.modal .dialog {
position: relative;
display: inline-block;
vertical-align: middle;
margin-left: auto;
margin-right: auto;
width: 680rpx;
max-width: 100%;
background-color: #fff;
border-radius: 10rpx;
overflow: hidden;
}
.modal.bottom-modal::before {
vertical-align: bottom;
}
.modal.bottom-modal .dialog {
width: 100%;
border-radius: 20rpx 20rpx 0 0;
background-color: #fff;
}
.modal.bottom-modal .dialog {
width: 100%;
border-radius: 20rpx 20rpx 0 0;
background-color: #fff;
}
.modal.bottom-modal {
margin-bottom: -1000rpx;
}
.modal.bottom-modal.show {
margin-bottom: 0;
}
.modal.drawer-modal {
transform: scale(1);
display: flex;
}
.modal.drawer-modal .dialog {
height: 100%;
min-width: 200rpx;
border-radius: 0;
margin: initial;
transition-duration: 0.3s;
}
.modal.drawer-modal.justify-start .dialog {
transform: translateX(-100%);
}
.modal.drawer-modal.justify-end .dialog {
transform: translateX(100%);
}
.modal.drawer-modal.show .dialog {
transform: translateX(0%);
}
.modal .dialog>.bar:first-child .action{
min-width: 100rpx;
/*margin-right: 0;*/
min-height: 100rpx;
}
.dialog-list+.dialog-list {
margin-top: 30rpx
}
.dialog-list>.dialog-list-item {
transition: all .6s ease-in-out 0s;
transform: translateX(0rpx)
}
.dialog-list.menu>.dialog-list-item {
position: relative;
display: flex;
padding: 0 30rpx;
min-height: 100rpx;
background-color: var(--white);
justify-content: space-between;
align-items: center
}
.dialog-list.menu>.dialog-list-item:last-child:after {
border: none
}
.dialog-list.menu>.dialog-list-item:after {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 200%;
height: 200%;
border-bottom: 1rpx solid #ddd;
border-radius: inherit;
content: " ";
transform: scale(.5);
transform-origin: 0 0;
pointer-events: none
}
.dialog-list.menu>.dialog-list-item.grayscale {
background-color: #f5f5f5
}
.dialog-list.menu>.dialog-list-item.cur {
background-color: #fcf7e9
}
.dialog-list.menu>.dialog-list-item.arrow {
padding-right: 90rpx
}
.dialog-list.menu>.dialog-list-item.arrow:before {
position: absolute;
top: 0;
right: 30rpx;
bottom: 0;
display: block;
margin: auto;
width: 30rpx;
height: 30rpx;
color: var(--grey);
content: "\e6a3";
text-align: center;
font-size: 34rpx;
font-family: "icon";
line-height: 30rpx
}
.dialog-list.menu>.dialog-list-item button.content {
padding: 0;
background-color: transparent;
justify-content: flex-start
}
.dialog-list.menu>.dialog-list-item button.content:after {
display: none
}
.dialog-list.menu>.dialog-list-item .content>view:first-child {
display: flex;
align-items: center
}
.dialog-list.menu>.dialog-list-item .content>text[class*=icon] {
display: inline-block;
margin-right: 10rpx;
width: 1.6em;
text-align: center
}
.dialog-list.menu>.dialog-list-item .content>image {
display: inline-block;
margin-right: 10rpx;
width: 1.6em;
height: 1.6em;
vertical-align: middle
}
.dialog-list.menu>.dialog-list-item .content {
font-size: 30rpx;
line-height: 1.6em;
flex: 1
}
================================================
FILE: miniprogram/style/base/nav.wxss
================================================
/* ==================
导航栏
==================== */
.tab {
white-space: nowrap;
}
::-webkit-scrollbar {
display: none;
}
.tab .item {
height: 90rpx;
display: inline-block;
line-height: 90rpx;
margin: 0 10rpx;
padding: 0 20rpx;
}
.tab .item.cur {
border-bottom: 4rpx solid;
}
================================================
FILE: miniprogram/style/base/shadow.wxss
================================================
/* ==================
阴影
==================== */
/* -- 形状阴影 -- */
.shadow[class*='white'] {
--ShadowSize: 0 1rpx 6rpx;
}
.shadow-large {
--ShadowSize: 0rpx 40rpx 100rpx 0rpx;
}
.shadow-warp {
position: relative;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
}
.shadow-warp:before,
.shadow-warp:after {
position: absolute;
content: "";
top: 20rpx;
bottom: 30rpx;
left: 20rpx;
width: 50%;
box-shadow: 0 30rpx 20rpx rgba(0, 0, 0, 0.2);
transform: rotate(-3deg);
z-index: -1;
}
.shadow-warp:after {
right: 20rpx;
left: auto;
transform: rotate(3deg);
}
.shadow-blur {
position: relative;
}
.shadow-blur::before {
content: "";
display: block;
background: inherit;
filter: blur(10rpx);
position: absolute;
width: 100%;
height: 100%;
top: 10rpx;
left: 10rpx;
z-index: -1;
opacity: 0.4;
transform-origin: 0 0;
border-radius: inherit;
transform: scale(1, 1);
}
.shadow {
box-shadow: var(--ShadowSize) var(--greyShadow);
}
.shadow[class*="-red"] {
box-shadow: var(--ShadowSize) var(--redShadow);
}
.shadow[class*="-orange"] {
box-shadow: var(--ShadowSize) var(--orangeShadow);
}
.shadow[class*="-yellow"] {
box-shadow: var(--ShadowSize) var(--yellowShadow);
}
.shadow[class*="-olive"] {
box-shadow: var(--ShadowSize) var(--oliveShadow);
}
.shadow[class*="-green"] {
box-shadow: var(--ShadowSize) var(--greenShadow);
}
.shadow[class*="-darkgreen"] {
box-shadow: var(--ShadowSize) var(--darkgreenShadow);
}
.shadow[class*="-cyan"] {
box-shadow: var(--ShadowSize) var(--cyanShadow);
}
.shadow[class*="-blue"] {
box-shadow: var(--ShadowSize) var(--blueShadow);
}
.shadow[class*="-purple"] {
box-shadow: var(--ShadowSize) var(--purpleShadow);
}
.shadow[class*="-mauve"] {
box-shadow: var(--ShadowSize) var(--mauveShadow);
}
.shadow[class*="-pink"] {
box-shadow: var(--ShadowSize) var(--pinkShadow);
}
.shadow[class*="-brown"] {
box-shadow: var(--ShadowSize) var(--brownShadow);
}
.shadow[class*="-grey"] {
box-shadow: var(--ShadowSize) var(--greyShadow);
}
.shadow[class*="-gray"] {
box-shadow: var(--ShadowSize) var(--grayShadow);
}
.shadow[class*="-black"] {
box-shadow: var(--ShadowSize) var(--blackShadow);
}
.shadow[class*="-white"] {
box-shadow: var(--ShadowSize) var(--blackShadow);
}
================================================
FILE: miniprogram/style/base/table.wxss
================================================
page {
--borderColor: #ccc;
}
.table {
position: relative;
font-size: 28rpx;
background: #fff;
border-right: none;
overflow: hidden;
}
.table.sticky {
overflow: visible;
}
.table .table-top {
width: 100%;
background-color: #fff;
}
.table.sticky .table-top {
width: 100%;
position: sticky;
top: 0;
z-index: 9999999;
}
.table .full {
flex-grow: 1
}
.thead {
border-bottom: none;
display: flex;
justify-content: flex-start;
overflow: visible;
color: #333;
border: 1px solid var(--borderColor);
box-sizing: border-box;
background-color: #f2f2f2;
}
.thead .td {
padding: 20rpx 10rpx;
font-weight: bold;
display: inline-block;
white-space: nowrap;
text-align: center;
border-right: 1rpx solid #fff;
}
.thead .td:last-child {
border-right: none;
}
.thead.border .td {
border-right: 1rpx solid var(--borderColor);
}
.thead.border .td:last-child {
border-right: none;
}
/* .tr{
display: flex;
white-space:nowrap;
} */
.tbody {
box-sizing: border-box;
font-size: 28rpx;
color: #444;
border: 1px solid var(--borderColor);
border-top: none;
}
.tbody .line {
padding: 25rpx 20rpx 10rpx;
text-align: center;
font-size: 28rpx;
border-bottom: 1rpx solid var(--borderColor);
}
.tbody .line>text {
position: relative;
}
.tbody .line>text::before {
content: '';
position: absolute;
right: 130%;
top: 50%;
width: 150rpx;
border-bottom: 1rpx dashed var(--borderColor);
}
.tbody .line>text::after {
content: '';
position: absolute;
left: 130%;
top: 50%;
width: 150rpx;
border-bottom: 1rpx dashed var(--borderColor);
}
.tbody .tr {
display: flex;
border-bottom: 1px solid var(--borderColor);
}
.tbody .tr:last-child {
border-bottom: none;
}
.tbody .tr:last-child {
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody .tr.stripe {
background: #fff;
border-bottom: none;
}
.tbody .tr.stripe:nth-child(2n) {
background: #F6F6F6;
}
.tbody .tr .td {
white-space: wrap;
padding: 20rpx 10rpx;
text-align: center;
}
.tbody .tr.border .td {
border-right: 1rpx solid var(--borderColor);
}
.tbody .tr.border .td:last-child {
border-right: none;
}
.table .no-data {
display: flex;
padding: 50rpx;
color: #666;
justify-content: center;
}
.table .table-form {
padding: 20rpx;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
}
.table .table-form .item {
width: 50%;
padding: 10rpx 5rpx;
display: flex;
justify-content: flex-start;
align-items: center;
}
.table .table-form .item .title {
font-weight: bold;
}
.table .table-form .item .content {
flex: 1;
border-bottom: 1rpx solid #ccc;
margin: 0 10rpx 0rpx 0;
padding: 0 0 0 5rpx;
height: 50rpx;
}
.table .table-form .item .content input {
width: 100%;
}
.table .table-form .oprt {
margin-top: 10rpx;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
}
.table .table-form .oprt>view {
padding: 5rpx 40rpx;
background-color: #f2f2f2;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
}
.table .table-form .button-hover {
opacity: .9;
transform: scale(0.95, 0.95);
position: relative;
top: 3rpx;
left: 3rpx;
box-shadow: 0px 0px 8px rgba(0, 0, 0, .1) inset;
}
.table .table-form .oprt .submit {
background-color: var(--adminColor);
color: #fff;
}
================================================
FILE: miniprogram/style/base/tag.wxss
================================================
/* ==================
标签
==================== */
.tag {
font-size: 24rpx;
vertical-align: middle;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 0rpx 16rpx;
height: 48rpx;
font-family: Helvetica Neue, Helvetica, sans-serif;
white-space: nowrap;
}
.tag:not([class*="bg"]):not([class*="border"]) {
background-color: var(--ghostWhite);
}
.tag[class*="border-"]::after {
content: " ";
width: 200%;
height: 200%;
position: absolute;
top: 0;
left: 0;
border: 1rpx solid currentColor;
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
border-radius: inherit;
z-index: 1;
pointer-events: none;
}
.tag.radius[class*="border"]::after {
border-radius: 12rpx;
}
.tag.round[class*="border"]::after {
border-radius: 1000rpx;
}
.tag[class*="border-"]::after {
border-radius: 0;
}
.tag.border-bold::after {
border: 6rpx solid currentColor;
}
.tag+.tag {
margin-left: 10rpx;
}
.tag.small {
font-size: 20rpx;
padding: 0rpx 12rpx;
height: 32rpx;
}
.capsule {
display: inline-flex;
vertical-align: middle;
}
.capsule+.capsule {
margin-left: 10rpx;
}
.capsule .tag {
margin: 0;
}
.capsule .tag[class*="border-"]:last-child::after {
border-left: 0rpx solid transparent;
}
.capsule .tag[class*="border-"]:first-child::after {
border-right: 0rpx solid transparent;
}
.capsule.radius .tag:first-child {
border-top-left-radius: 6rpx;
border-bottom-left-radius: 6rpx;
}
.capsule.radius .tag:last-child::after,
.capsule.radius .tag[class*="border-"] {
border-top-right-radius: 12rpx;
border-bottom-right-radius: 12rpx;
}
.capsule.round .tag:first-child {
border-top-left-radius: 200rpx;
border-bottom-left-radius: 200rpx;
text-indent: 4rpx;
}
.capsule.round .tag:last-child::after,
.capsule.round .tag:last-child {
border-top-right-radius: 200rpx;
border-bottom-right-radius: 200rpx;
text-indent: -4rpx;
}
.tag.badge {
border-radius: 200rpx;
position: absolute;
top: -10rpx;
right: -10rpx;
font-size: 20rpx;
padding: 0rpx 10rpx;
height: 28rpx;
color: var(--white);
}
.tag.badge:not([class*="bg-"]) {
background-color: #dd514c;
}
.tag:empty:not([class*="icon-"]) {
padding: 0rpx;
width: 16rpx;
height: 16rpx;
top: -4rpx;
right: -4rpx;
}
.tag[class*="icon-"] {
width: 32rpx;
height: 32rpx;
top: -4rpx;
right: -4rpx;
}
================================================
FILE: miniprogram/style/base/text.wxss
================================================
/* ==================
文本
==================== */
.text-xs,
[class*="icon-"].text-xs {
font-size: 20rpx!important;
}
.text-s,
[class*="icon-"].text-s {
font-size: 24rpx!important;
}
.text-normal,
[class*="icon-"].text-normal {
font-size: 28rpx!important;
font-weight:normal;
}
.text-l,
[class*="icon-"].text-l {
font-size: 32rpx!important;
}
.text-xl,
[class*="icon-"].text-xl {
font-size: 36rpx!important;
}
.text-xxl,
[class*="icon-"].text-xxl {
font-size: 44rpx!important;
}
.text-xxxl,
[class*="icon-"].text-xxxl {
font-size: 80rpx!important;
}
.text-xxxxl,
[class*="icon-"].text-xxxxl {
font-size: 120rpx!important;
}
.text-cut {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.content-cut {
text-overflow: ellipsis;
overflow: hidden;
}
.content-cut-one {
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
}
.content-cut-two {
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.content-cut-three {
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
.content-cut-four {
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;
}
.content-cut-five {
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
}
.text-bold {
font-weight: bold!important;
}
.text-center {
text-align: center!important;
}
.text-content {
line-height: 1.6!important;
}
.text-left {
text-align: left!important;
}
.text-right {
text-align: right!important;
}
.text-red,
.border-red {
color: var(--red)!important;
}
.text-orange,
.border-orange {
color: var(--orange)!important;
}
.text-yellow,
.border-yellow {
color: var(--yellow)!important;
}
.text-olive,
.border-olive {
color: var(--olive)!important;
}
.text-green,
.border-green {
color: var(--green)!important;
}
.text-darkgreen,
.border-darkgreen {
color: var(--darkgreen)!important;
}
.text-cyan,
.border-cyan {
color: var(--cyan)!important;
}
.text-blue,
.border-blue {
color: var(--blue)!important;
}
.text-purple,
.border-purple {
color: var(--purple)!important;
}
.text-mauve,
.border-mauve {
color: var(--mauve)!important;
}
.text-pink,
.border-pink {
color: var(--pink)!important;
}
.text-brown,
.border-brown {
color: var(--brown)!important;
}
.text-grey,
.border-grey {
color: var(--grey)!important;
}
.text-gray,
.border-gray {
color: var(--gray)!important;
}
.text-black,
.border-black {
color: var(--black)!important;
}
.text-white,
.border-white {
color: var(--white)!important;
}
================================================
FILE: miniprogram/style/project/admin_list_style.wxss
================================================
.admin-comm-list .item .info .oprt {
padding: 0rpx 0rpx;
display: flex;
justify-content: space-between;
}
.admin-comm-list .item .info .oprt .btn {
width: 155rpx;
margin-right: 0rpx !important;
height: 60rpx !important;
padding: 0 0rpx;
}
================================================
FILE: miniprogram/style/project/my_fav_style.wxss
================================================
.text-pic-list-box .item .simple .simple-title {
width: 100%;
}
================================================
FILE: miniprogram/style/project/my_foot_style.wxss
================================================
.text-pic-list-box .item .simple .simple-title {
width: 100%;
}
================================================
FILE: miniprogram/style/project/search_style.wxss
================================================
.main-search {
box-sizing: border-box;
width: 100%;
height: 100vh;
display: flex;
padding: 0;
flex-direction: column;
justify-content: center;
align-items: center;
}
.main-search .search {
width: 100%;
}
.main-search .his {
flex: 1;
margin-top: 2rpx;
width: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
padding: 30rpx;
}
.main-search .his .title {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
}
.main-search .his .title .tit {
font-size: 34rpx;
font-weight: bold;
flex: 1;
}
.main-search .his .title .del {
font-size: 32rpx;
color: #aaa;
margin-left: auto;
}
.main-search .his .search-content {
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
margin-top: 40rpx;
}
.main-search .his .search-content .btn {
margin-right: 30rpx;
margin-top: 30rpx;
font-size: 28rpx;
padding: 35rpx 45rpx;
background-color: #f2f2f2;
}
================================================
FILE: miniprogram/style/public/admin.wxss
================================================
page {
--adminColor: #2499f2;
--adminRedColor: #FF6666;
}
.main-admin {
width: 100%;
padding: 30rpx 20rpx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
box-sizing: border-box;
margin-bottom: 100rpx;
}
.main-admin form {
width: 100%;
}
.text-admin,
.text-blue {
color: var(--adminColor) !important;
}
.text-red {
color: var(--adminRedColor) !important;
}
.bg-admin,
.bg-blue {
background-color: var(--adminColor) !important;
color: #fff;
}
.bg-admin.light,
.bg-blue.light {
color: var(--adminColor) !important;
background-color: #cce6ff !important;
}
.border-admin,
.border-blue {
border-color: var(--adminColor) !important;
}
.form-box {
border-radius: 10rpx;
}
.btn {
border-radius: 10rpx;
}
.search-form.round {
border-radius: 10rpx;
}
.load.text-grey {
color: #999 !important;
}
.tabs {
color: #333 !important;
font-weight: bold;
}
.tabs .tab-menu.cur:after {
background-color: var(--adminColor) !important;
}
.tabs .cur {
color: var(--adminColor) !important;
}
.tabs .tab-menu.cur:after {
width: 80% !important;
}
.modal-admin .modal-content text {
text-align: left;
display: block;
}
.modal-admin .modal-content .pics {
margin-top: 30rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.modal-admin .modal-content .pics image {
flex: 1;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
.modal-admin .modal-content .modal-list {
flex: 1;
border-radius: 10rpx;
margin: 20rpx 0;
padding: 20rpx 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
}
.modal-admin .modal-content .modal-list .modal-list-item {
font-size: 26rpx;
line-height: 1.6;
display: flex;
flex-direction: column;
justify-content: flex-start;
width: 100%;
margin-bottom: 10rpx;
padding: 10rpx 0;
border-bottom: 2rpx dashed #ccc;
padding: 20rpx 40rpx;
}
.modal-admin .modal-content .modal-list .modal-list-item .item-title {
width: 100%;
text-align: left;
display: flex;
font-size: 32rpx;
line-height: 1.6;
margin-bottom: 10rpx;
}
.modal-admin .modal-content .item-content {
width: 100%;
text-align: left;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
line-height: 50rpx;
border-bottom: 2rpx dashed #ccc;
margin-bottom: 10rpx;
}
.modal-admin .modal-content .item-content text {
display: block;
}
.form-box .title-desc {
padding-left: 24rpx;
color: #666;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #eee;
}
.form-group .title {
font-size: 32rpx;
}
/** 通用列表 **/
.admin-comm-list {
width: 100%;
padding: 0rpx 20rpx 0rpx;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
.admin-comm-list .item {
width: 100%;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #fff;
border-radius: 5rpx;
overflow: hidden;
margin-bottom: 30rpx;
position: relative;
border: 1rpx solid #ccc;
}
.admin-comm-list .item .no {
position: absolute;
bottom: 0;
right: 10rpx;
z-index: 99999;
font-size: 38rpx;
font-weight: bold;
color: #ccc;
opacity: .5;
}
.admin-comm-list .item:last-child {
margin-bottom: 0;
}
.admin-comm-list .item .header {
width: 100%;
font-size: 28rpx;
text-align: left;
line-height: 2.6;
padding: 0 15rpx;
display: flex;
background-color: #f2f2f2;
justify-content: space-between;
align-items: center;
}
.admin-comm-list .item .header .left {
flex: 1;
font-size: 29rpx;
text-align: left;
line-height: 2.6;
padding: 0 5rpx;
text-align: left;
font-weight: bold;
}
.admin-comm-list .item .header .right {
min-width: 100rpx;
text-align: right;
}
.admin-comm-list .item .info {
width: 100%;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
background-color: #fff;
min-height: 150rpx;
padding: 15rpx 20rpx 30rpx;
position: relative;
}
.admin-comm-list .item .info .info-item {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: flex-start;
background-color: #fff;
line-height: 1.5;
font-size: 26rpx;
margin-bottom: 20rpx;
}
.admin-comm-list .item .info .info-item.info-item-solid {
border-bottom: 1rpx dashed #ddd;
padding: 10rpx 0;
}
.admin-comm-list .item .info .info-item .title {
width: 110rpx;
color: #333;
text-align: right;
font-weight: bold;
}
.admin-comm-list .item .info.title-mid .info-item .title {
width: 150rpx;
display: flex;
}
.admin-comm-list .item .info.title-long .info-item .title {
width: 200rpx;
}
.admin-comm-list .item .info .info-item .mao {
color: #333;
text-align: left;
margin-right: 10rpx;
}
.admin-comm-list .item .info .info-item .content {
flex: 1;
color: #555;
display: flex;
justify-content: flex-start;
align-items: flex-start;
display: flex;
flex-wrap: wrap;
}
.admin-comm-list .item .info .info-item .content image {
width: 150rpx;
height: 150rpx;
margin-right: 10rpx;
margin-bottom: 10rpx;
}
.admin-comm-list .item .info .oprt {
width: 100%;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
padding: 0rpx 20rpx;
}
.admin-comm-list .item .info .oprt .btn {
padding: 0 25rpx;
font-size: 26rpx;
border-radius: 8rpx !important;
line-height: unset;
height: 55rpx !important;
background-color: #eee !important;
margin-bottom: 20rpx;
}
.admin-comm-list .btn-more {
background-color: #fadbd9 !important;
color: #e54d42;
}
.btn-admin {
width: 90%;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
font-size: 32rpx;
line-height: 2.5;
background-color: var(--adminColor) !important;
color: #fff;
border-radius: 40rpx;
margin-bottom: 60rpx;
}
.btn-admin.button-hover {
opacity: .9;
transform: scale(0.95, 0.95);
position: relative;
top: 3rpx;
left: 3rpx;
box-shadow: 0px 0px 8px rgba(0, 0, 0, .1) inset;
}
.btn-bottom-admin {
width: 100%;
position: fixed;
bottom: 0;
text-align: center;
color: #fff;
line-height: 2.6;
font-size: 36rpx;
background-color: inherit;
padding-bottom: 0rpx;
/*没有安全区域的手机 */
padding-bottom: constant(safe-area-inset-bottom);
/*兼容有安全区域的手机*/
padding-bottom: env(safe-area-inset-bottom);
display: flex;
z-index: 99999;
}
.btn-bottom-admin>view {
width: 50%;
}
.btn-bottom-admin .return {
background-color: var(--adminRedColor);
}
.btn-bottom-admin .save {
background-color: var(--adminColor);
}
================================================
FILE: miniprogram/style/public/article_list.wxss
================================================
/*** 图文列表盒子 ***/
.text-pic-list-box {
width: 100%;
box-sizing: border-box;
padding: 20rpx 20rpx 30rpx 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.text-pic-list-box .item {
padding: 40rpx 30rpx 16rpx 40rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 30rpx;
position: relative;
}
/*扇形角标*/
.text-pic-list-box .item .corner {
position: absolute;
top: 0;
right: 0;
width: 80rpx;
height: 80rpx;
border-radius: 0 0rpx 0rpx 80rpx;
background-color: #EAEAEA;
font-size: 45rpx;
font-weight: bold;
color: #fff;
font-style: italic;
padding-top: 10rpx;
padding-left: 36rpx;
}
/*倾斜角标*/
.text-pic-list-box .item .lean-corner-box {
position: absolute;
width: 100%;
height: 100%;
right: 0;
top: 0;
overflow: hidden;
}
.text-pic-list-box .item .lean-corner-right-top {
background-color: green;
color: white;
width: 50%;
height: 40rpx;
line-height: 40rpx;
text-align: center;
margin-left: 70%;
margin-top: 18rpx;
position: absolute;
transform: rotate(45deg);
z-index: 1999;
font-size: 24rpx;
opacity: .6;
}
.text-pic-list-box .item .lean-corner-right-bottom {
background-color: green;
color: white;
width: 50%;
height: 40rpx;
line-height: 40rpx;
text-align: center;
margin-left: 70%;
margin-bottom: 18rpx;
position: absolute;
bottom: 0;
transform: rotate(-45deg);
z-index: 1999;
font-size: 24rpx;
opacity: .8;
}
.text-pic-list-box .item .status-right-top,
.text-pic-list-box .item .status-right-bottom {
position: absolute;
padding:0 20rpx;
height: 40rpx;
background-color: #f8f8f8;
font-size: 24rpx;
color: #aaa;
line-height: 40rpx;
text-align: center;
}
.text-pic-list-box .item .status-right-top {
top: 0;
right: 0;
border-top-right-radius: inherit;
border-bottom-left-radius: inherit;
}
.text-pic-list-box .item .status-right-bottom {
right: 0;
bottom: 0;
border-top-left-radius: inherit;
border-bottom-right-radius: inherit;
}
.text-pic-list-box .item .title {
max-height: 88rpx;
line-height: 44rpx;
font-size: 32rpx;
color: #000;
font-weight: bold;
z-index: 999;
}
.text-pic-list-box .item .title .tag {
font-size: 20rpx;
padding: 0rpx 8rpx;
height: 30rpx;
line-height: 30rpx;
opacity: .8;
}
.text-pic-list-box .item .author {
margin-top: 20rpx;
font-size: 24rpx;
display: flex;
justify-self: center;
align-items: center;
z-index: 999;
}
.text-pic-list-box .item .author .av {
margin-right: 5rpx;
width: 40rpx;
}
.text-pic-list-box .item .desc {
margin-top: 15rpx;
display: flex;
justify-content: center;
align-items: flex-start;
}
.text-pic-list-box .item .desc text {
max-height: 120rpx;
line-height: 40rpx;
font-size: 28rpx;
color: #777;
flex: 1;
}
.text-pic-list-box .item .desc .pic {
margin-left: 10rpx;
width: 200rpx;
height: 122rpx;
border-radius: 10rpx;
}
.text-pic-list-box .item .desc .pic.pic-left {
margin-left: 0rpx;
margin-right: 10rpx;
}
.text-pic-list-box .item .desc.pic-list {
justify-content: space-between;
}
.text-pic-list-box .item .desc.pic-list .pic {
margin-left: 0rpx;
}
.text-pic-list-box .item .data {
line-height: 1.5;
font-size: 28rpx;
color: #666;
margin-top: 10rpx;
display: flex;
justify-content: flex-start;
align-items: center;
}
.text-pic-list-box .item .data.data-button {
width: 100%;
justify-content: flex-end;
margin-bottom: 10rpx;
}
.text-pic-list-box .item .data.data-button button {
margin-left: 10rpx;
}
/*简约模式*/
.text-pic-list-box .item.item-simple {
padding: 24rpx;
}
.text-pic-list-box .item .simple {
width: 100%;
display: flex;
}
.text-pic-list-box .item .simple .simple-left {
width: 100rpx;
height: 100rpx;
background-color: #E4DACE;
border-radius: 20rpx;
margin-right: 52rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
}
.text-pic-list-box .item .simple .simple-right {
flex: 1;
display: flex;
flex-direction: column;
}
.text-pic-list-box .item .simple .simple-title {
font-size: 32rpx;
color: #000;
line-height: 1.5;
margin-top: 10rpx;
width: 450rpx;
}
.text-pic-list-box .item .simple .simple-desc {
margin-top: 20rpx;
font-size: 24rpx;
color: #999;
}
/**snake接龙**/
.text-pic-list-box .item.item-snake {
padding: 14rpx;
border-radius: 0rpx;
margin-bottom: 0;
border-bottom: 2rpx dashed #f2f2f2;
}
.text-pic-list-box .item .snake {
width: 100%;
display: flex;
}
.text-pic-list-box .item .snake .snake-left {
width: 40rpx;
height: 40rpx;
background-color: #E4DACE;
border-radius: 50%;
margin-right: 32rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
}
.text-pic-list-box .item .snake .snake-right {
flex: 1;
display: flex;
flex-direction: column;
}
.text-pic-list-box .item .snake .snake-title {
font-size: 28rpx;
color: #000;
line-height: 1.3;
margin-top: 5rpx;
width: 450rpx;
}
.text-pic-list-box .item .snake .snake-desc {
font-size: 24rpx;
color: rgb(203, 203, 203);
}
/* 左大图模式 */
.text-pic-list-box .item.item-leftbig {
padding: 0;
max-height: 230rpx;
}
.text-pic-list-box .item-leftbig .leftbig-card {
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
}
.text-pic-list-box .item-leftbig .leftbig-card .leftbig-left {
width: 180rpx;
height: 230rpx;
border-top-left-radius: 20rpx;
border-bottom-left-radius: 20rpx;
}
.text-pic-list-box .item-leftbig .leftbig-card .leftbig-right {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 20rpx;
margin-right: 20rpx;
}
.text-pic-list-box .item-leftbig .leftbig-card .leftbig-title {
font-size: 32rpx;
color: #000;
line-height: 1.5;
margin-top: 10rpx;
width: 500rpx;
font-weight: bold;
}
.text-pic-list-box .item-leftbig .leftbig-card .leftbig-title.long {
width: 650rpx;
}
.text-pic-list-box .item-leftbig .leftbig-card .leftbig-desc {
margin-top: 20rpx;
font-size: 24rpx;
color: #aaa;
}
.text-pic-list-box .item-leftbig .leftbig-card .data {
margin-top: 30rpx;
color: #888;
padding-bottom: 25rpx;
}
================================================
FILE: miniprogram/style/public/comm_box_list.wxss
================================================
/*** 图文列表盒子 ***/
.comm-list-box {
width: 100%;
box-sizing: border-box;
padding: 20rpx 30rpx 30rpx 30rpx;
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
}
.comm-list-box .item {
padding: 0rpx 0rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 30rpx;
position: relative;
}
.comm-list-box .bottom-tag {
background-color: #f2f2f2;
padding: 6rpx 15rpx;
border-radius: 8rpx;
}
/* 左大图模式 */
.comm-list-box .item.item-leftbig {
max-height: 230rpx;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-direction: row;
}
.comm-list-box .item-leftbig .leftbig-left {
width: 200rpx;
height: 230rpx;
border-top-left-radius: inherit;
border-bottom-left-radius: inherit;
}
.comm-list-box .item-leftbig .leftbig-right {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 20rpx;
margin-right: 20rpx;
justify-content: center;
align-items: flex-start;
position: relative;
padding: 15rpx 10rpx;
}
.comm-list-box .item-leftbig .leftbig-right .leftbig-title {
width: 100%;
font-size: 34rpx;
color: #000;
line-height: 1.5;
margin-top: 0rpx;
font-weight: bold;
}
.comm-list-box .item-leftbig .leftbig-desc {
margin-top: 5rpx;
font-size: 26rpx;
color: #666;
line-height: 1.4;
height: 107rpx;
margin-bottom: 10rpx;
}
.comm-list-box .item-leftbig .data {
width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 0rpx;
color: #bbb;
font-size: 24rpx;
position: relative;
min-height: 40rpx;
}
.comm-list-box .item-leftbig .data .bottom-tag-list {
margin-top: 0rpx;
font-size: 24rpx;
}
.comm-list-box .item-leftbig .leftbig-right .bottom-status {
font-size: 24rpx;
}
/* 大文字模式 */
.comm-list-box .item.item-bigtext {
height: 220rpx;
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
position: relative;
background: #fff;
color: #333;
padding: 0 40rpx;
}
.comm-list-box .item-bigtext .left {
margin-right: 20rpx;
width: 100rpx;
height: 100rpx;
background-color: #fff;
border-radius: 50%;
}
.comm-list-box .item-bigtext .left image {
border-radius: 50%;
width: 100rpx;
height: 100rpx;
}
.comm-list-box .item-bigtext .right {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
.comm-list-box .item-bigtext .right .bigtext-title {
width: 100%;
font-size: 36rpx;
font-weight: bold;
text-align: left;
}
.comm-list-box .item-bigtext .right .data-desc {
width: 100%;
font-size: 28rpx;
opacity: .7;
}
.comm-list-box .item-bigtext .data-status {
font-size: 24rpx;
position: absolute;
bottom: 10rpx;
right: 40rpx;
opacity: .8;
}
/* 上下图模式 */
.comm-list-box .item.item-upimg {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: relative;
background: #fff;
color: #000;
padding: 0 0rpx 10rpx;
overflow: hidden;
}
.comm-list-box .item-upimg .upimg-title {
width: 100%;
font-size: 38rpx;
font-weight: bold;
text-align: left;
padding: 10rpx 20rpx;
}
.comm-list-box .item-upimg image {
width: 100%;
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.comm-list-box .item-upimg .data-desc {
width: 100%;
font-size: 28rpx;
color: #666;
padding: 10rpx 20rpx;
max-height: 90rpx;
}
.comm-list-box .item-upimg .data-status {
width: 100%;
font-size: 24rpx;
color: #bbb;
padding-right: 20rpx;
text-align: right;
}
/* 左大图模式1 */
.comm-list-box .item.item-leftbig1 {
max-height: 200rpx;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: row;
margin-bottom: 20rpx;
padding: 20rpx;
}
.comm-list-box .item-leftbig1 .leftbig-left {
width: 240rpx;
height: 160rpx;
border-radius: 10rpx !important;
overflow: hidden;
}
.comm-list-box .item-leftbig1 .leftbig-right {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 20rpx;
justify-content: flex-start;
align-items: flex-start;
position: relative;
height: 160rpx;
}
.comm-list-box .item-leftbig1 .leftbig-right .leftbig-title {
width: 100%;
font-size: 34rpx;
color: #000;
font-weight: bold;
}
.comm-list-box .item-leftbig1 .leftbig-desc {
margin-top: 5rpx;
font-size: 27rpx;
color: #666;
}
.comm-list-box .item-leftbig1 .data {
width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
color: #bbb;
font-size: 24rpx;
position: absolute;
bottom: 0;
left: 0;
}
.comm-list-box .item-leftbig1 .data .bottom-tag-list {
margin-top: 0rpx;
font-size: 24rpx;
}
.comm-list-box .item-leftbig1 .leftbig-right .bottom-status {
font-size: 24rpx;
}
/* 左大图模式2 */
.comm-list-box .item.item-leftbig2:first-child {
margin-top: 60rpx;
}
.comm-list-box .item.item-leftbig2 {
max-height: 230rpx;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-direction: row;
margin-bottom: 70rpx;
}
.comm-list-box .item-leftbig2 .leftbig-left {
width: 200rpx;
height: 250rpx;
margin-left: 25rpx;
margin-top: -45rpx;
border-radius: 10rpx !important;
overflow: hidden;
}
.comm-list-box .item-leftbig2 .leftbig-right {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 20rpx;
margin-right: 20rpx;
justify-content: center;
align-items: flex-start;
position: relative;
padding: 15rpx 10rpx;
}
.comm-list-box .item-leftbig2 .leftbig-right .leftbig-title {
width: 100%;
font-size: 34rpx;
color: #000;
line-height: 1.5;
margin-top: 0rpx;
font-weight: bold;
}
.comm-list-box .item-leftbig2 .leftbig-desc {
margin-top: 5rpx;
font-size: 28rpx;
color: #666;
line-height: 1.4;
height: 107rpx;
margin-bottom: 10rpx;
}
.comm-list-box .item-leftbig2 .data {
width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 0rpx;
color: #999;
font-size: 24rpx;
position: relative;
min-height: 40rpx;
}
.comm-list-box .item-leftbig2 .data .bottom-tag-list {
margin-top: 0rpx;
font-size: 24rpx;
}
.comm-list-box .item-leftbig2 .leftbig-right .bottom-status {
font-size: 24rpx;
}
/* 左大图模式3 */
.comm-list-box .item.item-leftbig3:first-child {
margin-top: 60rpx;
}
.comm-list-box .item.item-leftbig3 {
max-height: 180rpx;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-direction: row;
margin-bottom: 70rpx;
}
.comm-list-box .item-leftbig3 .leftbig-left {
width: 190rpx;
height: 210rpx;
margin-left: 0rpx;
margin-top: -30rpx;
border-radius: 15rpx !important;
overflow: hidden;
box-shadow: var(--ShadowSize) var(--greyShadow);
}
.comm-list-box .item-leftbig3 .leftbig-right {
flex: 1;
display: flex;
flex-direction: column;
margin-left: 20rpx;
margin-right: 20rpx;
justify-content: center;
align-items: flex-start;
position: relative;
padding: 15rpx 10rpx;
}
.comm-list-box .item-leftbig3 .leftbig-right .leftbig-title {
width: 100%;
font-size: 34rpx;
color: #000;
line-height: 1.5;
margin-top: 0rpx;
font-weight: bold;
}
.comm-list-box .item-leftbig3 .leftbig-desc {
margin-top: 5rpx;
font-size: 28rpx;
color: #666;
line-height: 1.4;
height: 60rpx;
margin-bottom: 10rpx;
}
.comm-list-box .item-leftbig3 .data {
width: 100%;
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 0rpx;
color: #999;
font-size: 24rpx;
position: relative;
min-height: 40rpx;
}
.comm-list-box .item-leftbig3 .data .bottom-tag-list {
margin-top: 0rpx;
font-size: 24rpx;
}
.comm-list-box .item-leftbig3 .leftbig-right .bottom-status {
font-size: 24rpx;
}
/*右边/左边图*/
.comm-list-box .item.item-rightpic,
.comm-list-box .item.item-leftpic {
padding: 40rpx 30rpx 16rpx 40rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 20rpx;
position: relative;
}
.comm-list-box .item-rightpic .title,
.comm-list-box .item-leftpic .title {
max-height: 88rpx;
line-height: 44rpx;
font-size: 34rpx;
color: #000;
font-weight: bold;
z-index: 999;
}
.comm-list-box .item-rightpic .desc,
.comm-list-box .item-leftpic .desc {
margin-top: 32rpx;
display: flex;
justify-content: center;
align-items: flex-start;
}
.comm-list-box .item-rightpic .desc text,
.comm-list-box .item-leftpic .desc text {
max-height: 120rpx;
line-height: 40rpx;
font-size: 28rpx;
color: #666;
flex: 1;
}
.comm-list-box .item-rightpic .desc .pic {
margin-left: 15rpx;
width: 180rpx;
height: 122rpx;
border-radius: 10rpx;
}
.comm-list-box .item-leftpic .desc .pic {
margin-left: 0rpx;
margin-right: 15rpx;
width: 180rpx;
height: 122rpx;
border-radius: 10rpx;
}
.comm-list-box .item-rightpic .data,
.comm-list-box .item-leftpic .data {
height: 24rpx;
line-height: 24rpx;
font-size: 24rpx;
color: #bbb;
margin-top: 30rpx;
display: flex;
justify-self: center;
align-items: center;
}
/*横向*/
.comm-list-scroll {
width: 100%;
margin-top: 10rpx;
background-color: #fff;
white-space: nowrap;
}
.comm-list-scroll .item-scroll {
display: inline-block;
padding: 10rpx 25rpx 10rpx 0rpx;
}
.comm-list-scroll .item-scroll image {
width: 300rpx;
height: 185rpx;
border-radius: 10rpx;
}
.comm-list-scroll .item-scroll .title {
font-size: 28rpx;
width: 300rpx;
height: 40rpx;
color:#000;
}
/*瀑布流*/
.comm-list-flow {
margin-top: 10rpx;
background-color: #fff;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
padding: 0 20rpx;
}
.comm-list-flow .item-flow {
width: 50%;
padding: 10rpx 15rpx 20rpx;
}
.comm-list-flow .item-flow .item-flow-inner {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.comm-list-flow .item-flow .item-flow-inner image {
width: 100%;
height: 420rpx;
border-radius: 10rpx;
border: 1rpx solid #ddd;
}
.comm-list-flow .item-flow .item-flow-inner .title-flow {
width: 100%;
text-align: left;
color: #333;
font-size: 28rpx;
font-weight: bold;
margin-top: 20rpx;
}
/*文字条目*/
.comm-list-box .item-line {
width: 100%;
background-color: #fff;
padding: 30rpx 20rpx;
display: flex;
border-bottom: 1rpx solid #ddd;
justify-content: center;
}
.comm-list-box .item-line:last-child {
border-bottom: unset;
}
.comm-list-box .item-line .left {
flex: 1;
display: flex;
align-items: center;
}
.comm-list-box .item-line .left .order {
min-width: 50rpx;
padding: 0 10rpx;
height: 35rpx;
display: flex;
margin-right: 10rpx;
align-items: center;
justify-content: center;
font-size: 24rpx;
border-bottom-left-radius: 10rpx;
border-top-right-radius: 10rpx;
}
.comm-list-box .item-line .left .title {
flex: 1;
font-size: 34rpx;
color: #000;
}
.comm-list-box .item-line .right {
width: 40rpx;
font-size: 26rpx;
text-align: right;
color: #999;
}
================================================
FILE: miniprogram/style/public/detail.wxss
================================================
/*** 详情盒子 ***/
.article-box {
padding: 0;
box-sizing: border-box;
width: 100%;
}
.article-box .article {
background-color: #fff;
padding: 36rpx 26rpx;
border-radius: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.article-box .article.join {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
}
.article-box .article.join .left {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.article-box .article.join .right {
width: 30rpx;
}
.article-box .article.join .left .btn {
width: 100%;
}
.article-box .article.join .avatar-group {
direction: unset;
display: flex;
justify-content: flex-end;
align-items: center;
}
.article-box .article .title {
font-size: 38rpx;
line-height: 1.5;
color: #000;
font-weight: bold;
z-index: 999;
}
.article-box .article .title-sub {
font-size: 32rpx;
line-height: 1.5;
color: #000;
font-weight: bold;
z-index: 999;
}
.article-box .article .time {
font-size: 24rpx;
line-height: 1.5;
color: #999;
margin-top: 20rpx;
}
.article-box .article .hint {
box-sizing: border-box;
width: 100%;
line-height: 1.8;
background-color: #f8f8f8;
border-radius: 20rpx;
color: #aaa;
padding: 10rpx 20rpx;
font-size: 24rpx;
}
.article-box .article .address {
margin: 20rpx 0rpx;
box-sizing: border-box;
width: 100%;
line-height: 1.6;
display: flex;
justify-content: flex-start;
align-items: flex-start;
padding: 10rpx 0rpx 10rpx;
background-color: #f8f8f8;
border-radius: 10rpx;
}
.article-box .article .address .address-left {
width: 50rpx;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.article-box .article .address .address-left .icon-location {
font-size: 26rpx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
}
.article-box .article .address .address-right {
height: 100%;
flex: 1;
font-size: 26rpx;
font-weight: bold;
color: #666;
}
.article-box .article .address .address-end {
height: 100%;
width: 40rpx;
font-size: 26rpx;
color: #ccc;
display: flex;
justify-content: flex-start;
align-items: center;
}
.article-box .article .user-card {
margin-top: 50rpx;
display: flex;
justify-content: flex-start;
}
.article-box .article .user-card .avatar {
width: 100rpx;
height: 100rpx;
}
.article-box .article .user-card .detail {
margin-left: 30rpx;
display: flex;
flex-direction: column;
flex: 1;
}
.article-box .article .user-card .detail .name {
height: 1;
font-size: 30rpx;
width: 350rpx;
font-weight: bold;
}
.article-box .article .user-card .detail .last {
margin-top: 15rpx;
height: 1;
width: 350rpx;
font-size: 26rpx;
color: #aaa;
}
.article-box .article .user-card .view-btn {
width: 120rpx;
display: flex;
justify-content: flex-end;
align-items: center;
}
.article-box .article .content {
margin-top: 24rpx;
color: #101010;
font-size: 30rpx;
text-align: justify;
line-height: 1.6;
}
.article-box .article .pics {
margin-top: 30rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.article-box .article .pics image {
width: 100%;
border-radius: 10rpx;
margin-bottom: 0rpx;
}
.article-box .comment {
margin-top: 24rpx;
margin-bottom: 150rpx;
background-color: #fff;
padding: 10rpx 36rpx 36rpx;
border-radius: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.article-box .comment .title {
font-size: 28rpx;
line-height: 2.4;
font-weight: bold;
border-bottom: 4rpx solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.article-box .comment .list {
display: flex;
justify-content: center;
align-items: center;
padding: 20rpx 0rpx 20rpx 0rpx;
margin-bottom: 20rpx;
border-bottom: 2rpx solid #eee;
}
.article-box .comment .list .avatar {
margin-right: 20rpx;
align-self: start;
}
.article-box .comment .list .detail {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.article-box .comment .list .detail .name {
height: 36rpx;
font-size: 30rpx;
font-weight: bold;
display: flex;
align-items: flex-end;
}
.article-box .comment .list .detail .name .name-real {
max-width: 200rpx;
}
.article-box .comment .list .detail .name .classmate {
margin-left: 20rpx;
max-width: 250rpx;
font-size: 24rpx;
color: #ccc;
}
.article-box .comment .list .detail .comment-content {
font-size: 28rpx;
margin-top: 30rpx;
line-height: 1.6;
}
.article-box .comment .list .detail .last {
font-size: 26rpx;
margin-top: 16rpx;
color: #aaa;
display: flex;
justify-content: space-between;
align-items: baseline;
}
.article-box .comment .list .detail .last .time {
font-size: 24rpx;
margin-top: 16rpx;
color: #aaa;
}
.article-box .article-bottom {
position: fixed;
height: 120rpx;
background-color: #fff;
left: 0;
bottom: 0;
width: 100%;
box-sizing: border-box;
display: flex;
padding: 22rpx 28rpx;
justify-content: center;
align-items: center;
border-top: 6rpx solid #eee;
}
.article-box .article-bottom .input-comment {
flex: 1;
height: 62rpx;
background-color: #f8f8f8;
border-radius: 100rpx;
display: flex;
justify-content: flex-start;
align-items: center;
font-size: 30rpx;
}
.article-box .article-bottom .data {
margin-left: 10rpx;
width: 500rpx;
font-size: 32rpx;
color: #ccc;
display: flex;
justify-content: flex-end;
align-items: center;
}
.article-box .article-bottom .share-btn {
background-color: transparent !important;
padding: 0 !important;
line-height: inherit !important;
margin: 0 0 0 30rpx;
width: auto !important;
font-weight: 500 !important;
border-radius: none !important;
font-size: 32rpx;
color: #333;
}
.article-box .article-bottom .share-btn::after {
border: none !important;
padding: 0 !important;
margin: 0 !important;
}
.article-box .comment-modal .bar {
display: flex;
align-items: baseline
}
.article-box .comment-modal .comment-textarea .form-group textarea {
margin: 2rpx 0 30rpx;
}
.article-box .article .corner-right-top {
position: absolute;
top: 0;
right: 0;
width: 150rpx;
height: 60rpx;
border-radius: 0 20rpx 0 20rpx;
background-color: #f8f8f8;
font-size: 28rpx;
color: #aaa;
line-height: 60rpx;
text-align: center;
}
/*倾斜角标*/
.article-box .article .lean-corner-box {
position: absolute;
width: 100%;
height: 100rpx;
right: 0;
top: 0;
overflow: hidden;
z-index: 1;
}
.article-box .article .lean-corner-right-top {
background-color: green;
color: white;
width: 50%;
height: 40rpx;
line-height: 40rpx;
text-align: center;
margin-left: 70%;
margin-top: 18rpx;
position: absolute;
transform: rotate(45deg);
z-index: 1999;
font-size: 24rpx;
opacity: .8;
}
.article-box .article .lean-corner-right-bottom {
background-color: green;
color: white;
width: 50%;
height: 40rpx;
line-height: 40rpx;
text-align: center;
margin-left: 70%;
margin-bottom: 18rpx;
position: absolute;
bottom: 0;
transform: rotate(-45deg);
z-index: 1999;
font-size: 24rpx;
opacity: .8;
}
================================================
FILE: miniprogram/style/public/project.wxss
================================================
/* 主框架 */
.main {
width: 100%;
box-sizing: border-box;
padding: 20rpx 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 70rpx;
}
/*表单*/
.main form {
width: 100%;
display: flex;
flex-direction: column;
}
.form-group .title {
font-weight: bold;
}
.form-group .title.must::before {
color: red;
content: "*";
}
.form-box {
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 24rpx;
box-sizing: border-box;
width: 100%;
}
/* 固定按钮 */
.btn-fixed {
position: fixed;
bottom: 130rpx;
right: 12rpx;
color: #fff;
font-size: 40rpx;
font-weight: bold;
border-radius: 50%;
background-color: #ccc;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
/*回顶部按钮*/
.btn-top {
border: 1rpx solid #ccc;
background-color: #f8f8f8 !important;
}
/*单行大按钮*/
.btn-main {
width: 600rpx;
font-size: 32rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
background-color: #0E9489;
color: #fff;
font-weight: bold;
}
.clearbtn {
margin: 0;
padding: 0;
background-color: transparent;
text-align: center;
font-size: inherit;
color: inherit;
box-sizing: border-box;
text-align: center;
text-decoration: none;
line-height: unset;
border-radius: 0;
-webkit-tap-highlight-color: transparent;
overflow: hidden;
font-weight: normal;
}
.clearbtn::after {
border: none !important;
border-radius: 0;
}
/*非分包菜单*/
.tab-bar-home {
position: fixed;
top: 50rpx;
left: 20rpx;
width: 150rpx;
height: 150rpx;
z-index: 999999;
display: flex;
align-items: center;
justify-content: center;
opacity: .8;
display: flex;
align-items: flex-start;
justify-content: flex-start;
}
.tab-bar-home .tab-bar-home-text {
background: #fff;
border-radius: 50%;
font-size: 36rpx;
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
}
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fefefe;
display: flex;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
z-index: 99999;
}
.tab-bar-border {
background-color: rgba(0, 0, 0, 0.33);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
}
.tab-bar-item {
flex: 1;
text-align: center;
display: flex;
justify-content: flex-start;
align-items: center;
flex-direction: column;
position: relative;
padding-top: 10rpx;
}
.tab-bar-item .tab-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 2rpx;
}
.tab-bar-item .tab-text {
font-size: 24rpx !important;
color: #999999;
}
/**分包距离底部 */
.sub-margin-bottom {
margin-bottom: 100rpx;
}
.btn-base {
padding: 0 40rpx;
font-size: 32rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 14rpx;
width: 100%;
color: #fff;
background-color: var(--projectColor);
font-weight: bold;
margin-top: 10rpx;
margin-bottom: 20rpx;
}
.btn-base.button-hover {
opacity: .9;
transform: scale(0.95, 0.95);
position: relative;
top: 3rpx;
left: 3rpx;
box-shadow: 0px 0px 8px rgba(0, 0, 0, .1) inset;
}
.phone-button {
color: #333 !important;
background-color: var(--greyLight) !important;
}
/*cmpt-detail底部菜单*/
.cmpt-biz-detail-mode2-btn {
width: 100%;
border-radius: 10rpx;
background-color: var(--projectColor);
line-height: 80rpx;
color: #fff;
z-index: 9991;
font-size: 32rpx;
}
================================================
FILE: miniprogram/tpls/project/about_tpl.wxml
================================================
{{item.val}}
v{{accountInfo.miniProgram.version}} {{accountInfo.miniProgram.envVersion}}
================================================
FILE: miniprogram/tpls/project/my_fav_tpl.wxml
================================================
{{item.FAV_TITLE}}
{{item.FAV_TYPE}}
{{item.FAV_ADD_TIME}}
================================================
FILE: miniprogram/tpls/project/my_foot_tpl.wxml
================================================
暂无记录哦~
{{item.title}}
{{item.type}}
{{item.time}}
================================================
FILE: miniprogram/tpls/project/news_index_tpl.wxml
================================================
================================================
FILE: miniprogram/tpls/project/search_tpl.wxml
================================================
历史记录
{{item}}
================================================
FILE: miniprogram/tpls/public/admin_forms_detail_tpl.wxml
================================================
{{item.title}}
:
{{item.val===true?'是':'否'}}
{{item.val}}
================================================
FILE: miniprogram/tpls/public/base_list_tpl.wxml
================================================
{{item.title}}
{{index+1}}
{{item.title}}
{{item.title}}
{{item.title}}
{{item.desc}}
{{item.ext}}
{{item.title}}
{{item.desc}}
{{item.ext}}
{{item.title}}
{{item.desc}}
{{item.ext}}
{{item.title}}
{{item.desc}}
{{item.ext}}
{{item.title}}
{{item.desc}}
{{item.ext}}
================================================
FILE: miniprogram/tpls/public/list_load_tpl.wxml
================================================
{{noHint?noHint:'暂无记录哦~'}}
================================================
FILE: miniprogram/tpls/public/top_tpl.wxml
================================================
================================================
FILE: miniprogram/tpls/wxs/tools.wxs
================================================
function indexOf(data, key) {
return data.indexOf(key);
}
function includes(data, key) {
return data.indexOf(key) > -1 ? true : false;
}
function split(str, chr) {
var arr = str.split(chr);
if (arr.length == 0) return [];
return arr;
}
module.exports = {
split: split,
indexOf: indexOf,
includes: includes
};
module.exports.msg = "hello tools";
================================================
FILE: project.config.json
================================================
{
"miniprogramRoot": "miniprogram/",
"cloudfunctionRoot": "cloudfunctions/",
"setting": {
"urlCheck": false,
"es6": true,
"enhance": true,
"postcss": true,
"preloadBackgroundData": false,
"minified": true,
"newFeature": true,
"coverView": true,
"nodeModules": false,
"autoAudits": false,
"showShadowRootInWxmlPanel": true,
"scopeDataCheck": false,
"uglifyFileName": true,
"checkInvalidKey": true,
"checkSiteMap": true,
"uploadWithSourceMap": true,
"compileHotReLoad": false,
"lazyloadPlaceholderEnable": false,
"useMultiFrameRuntime": true,
"useApiHook": true,
"useApiHostProcess": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"useIsolateContext": false,
"userConfirmedUseIsolateContext": true,
"userConfirmedBundleSwitch": false,
"packNpmManually": false,
"packNpmRelationList": [],
"minifyWXSS": true,
"disableUseStrict": false,
"minifyWXML": true,
"showES6CompileOption": false,
"useCompilerPlugins": false,
"ignoreUploadUnusedFiles": true,
"useStaticServer": true
},
"appid": "wxb100b44af794708e",
"projectname": "Multi多功能平台",
"libVersion": "2.17.0",
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"cloudfunctionTemplateRoot": "cloudfunctionTemplate/",
"compileType": "miniprogram",
"srcMiniprogramRoot": "miniprogram/",
"packOptions": {
"ignore": [],
"include": []
},
"editorSetting": {
"tabIndent": "tab",
"tabSize": 4
},
"description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"condition": {}
}
================================================
FILE: project.private.config.json
================================================
{
"setting": {
"compileHotReLoad": true,
"urlCheck": false
},
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "%E6%97%85%E6%B8%B8%E6%99%AF%E5%8C%BA1-github",
"condition": {},
"libVersion": "2.17.0"
}