作這個小程序的本意是,我曾經參加過我家鄉的志願者活動,而後加入的志願者組織是家鄉獨自成立的一支javascript
有着上千名成員的志願者團隊。由於名爲藍天高涼志願服務隊,因此起了名字叫藍天籌,但願能作出一個爲家鄉服務的小程序。css
首頁顯示的功能:顯示全部(由藍天志願隊的會長或部長髮起的衆籌項目,這樣確保都是通過組織上鑑定和實地考察幫助者的真實性)html
首頁有根據不一樣類型的排序功能,好比根據不一樣衆籌項目的類型,孤寡老人,貧困學生,留守兒童等。前端
還有根據衆籌項目的進展進度排序,有未完成,即將完成,已完成,已結束。(根據當前衆籌金額與目標籌集金額作比例運算,而動態修改類型)java
有根據目標籌集金額的高低排序mysql
首頁具備上拉到底加載更多的功能。linux
底部導航欄的第二個爲添加項目(設計爲只能經過管理員帳號登錄實現,確保項目的真實性,必須由志願者組織發起)nginx
添加項目詳情頁則填寫一些幫助者的信息,詳情,上傳相關圖片。git
首頁裏點擊具體項目,能跳轉項目詳情頁,能查看項目和幫助者的信息,還能查看照片。web
在詳情頁具備我要幫幫他的按鈕,(設計爲模擬捐款和留言的功能)
籌集人的微信頭像和暱稱,還有衆籌金額,留言都會顯示在詳情頁。
技術選型:
加密:
public class PasswordEncryptor { //這是一個自定義的hexDigits,若是黑客不知道這串東西,是不能窮舉破解出來的,知道salt也沒用 private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "!", "#", "@", "a", "b", "c", "d", "*", "f", "g", "F"}; private String salt; //salt private String algorithm; //散列算法 public PasswordEncryptor(String salt,String algorithm) { this.salt = salt; this.algorithm = algorithm; } //加密 public String encode(String rawPassword){ try { MessageDigest digest = MessageDigest.getInstance(algorithm); return byteArrayToHex(digest.digest(mergePasswordAndSalt(rawPassword).getBytes("UTF-8"))); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } //合併salt + password private String mergePasswordAndSalt(String rawPassword){ if(rawPassword==null){ rawPassword = ""; } if(salt.equals("")||salt==null){ return rawPassword; }else{ return rawPassword+"{"+salt+"}"; //用特殊的方式拼接salt } } /** * 字節數組轉16進制 */ private static String byteArrayToHex(byte[] b){ StringBuffer stringBuffer = new StringBuffer(); for(int i=0;i<b.length;i++){ stringBuffer.append(byteToHex(b[i])); } return stringBuffer.toString(); } private static String byteToHex(byte b){ int n = b; if(n<0){ n+=256; } int d1 = n / hexDigits.length; int d2 = n % hexDigits.length; return hexDigits[d1]+hexDigits[d2]; } //初始的管理員密碼 public static void main(String[] args) { String salt = UUID.randomUUID().toString(); PasswordEncryptor encoderMd5 = new PasswordEncryptor(salt, "sha-256"); String encodedPassword = encoderMd5.encode("csyzhanpeng123456"); System.out.println("加密後密碼:" + encodedPassword + "\n密碼長度:" + encodedPassword.length()); System.out.println("salt:" + salt); } }
先後端校驗
@Service public class UserPasswordServiceImpl implements UserPasswordService { @Autowired private SysAdminMapper sysAdminMapper; @Override public boolean isValid(String username, String password) { SysAdminExample example = new SysAdminExample(); example.or().andUsernameEqualTo(username); List<SysAdmin> admins = sysAdminMapper.selectByExample(example); SysAdmin sysAdmin = new SysAdmin(); //說明找到了這個username,後面就是檢測密碼 if(admins!=null&&admins.size()!=0){ sysAdmin = admins.get(0); //校驗 PasswordEncryptor encryptor = new PasswordEncryptor(sysAdmin.getSalt(), "sha-256"); String encodePassword = encryptor.encode(password); if(encodePassword.equals(sysAdmin.getPassword())){ return true; }else{ return false; } }else{ return false; } } }
salt+password參考連接:http://www.cnblogs.com/xiaochangwei/p/5942456.html
前端經過存在本地緩存,緩存管理員的登錄態。
經過在page的onShow生命週期,經過判斷緩存,來達到攔截頁面(檢測是否具備權限)
onShow:function(e){ let that = this; wx.getStorage({ key: 'login_key', success: function(res) { wx.request({ url: baseUrl + 'item/itemTypes', method: "GET", success: function (res) { console.log(res); that.setData({ itemTypes: res.data.extend.itemTypes }) } }) }, fail:function(){ wx.navigateTo({ url: '../login/login', }) } }) },
formSubmit: function (e) { wx.showLoading({ title: '登陸中...', }) console.log(e); this.setData({ disabled: true }); wx.request({ url: baseUrl+"login", method:"POST", data: { username: e.detail.value.no, password: e.detail.value.pwd }, header: { 'content-type': 'application/x-www-form-urlencoded' }, success: function (res) { console.log(res); if (res.data.code == 200) { // 設置本地緩存 wx.setStorageSync('login_key', res.data.extend.login_key); wx.showToast({ title: "管理員登陸成功", icon: 'success', duration: 2000 }) setTimeout(function () { wx.switchTab({ url: '../add/add', }) }, 2000) } else { wx.showToast({ title: "用戶名或密碼錯誤", icon: 'none', duration: 2000 }) } } }) },
功能:三級級聯菜單 項目類型 + 項目進度狀況 + 按目標籌集金額從低到高(從高到低)
總結:
下拉小按鈕
/* 這裏的icon用border渲染出來,而不是用字體圖標 */ .icon{ margin-left: 10rpx; margin-top: 16rpx; display: inline-block; border: 10rpx solid transparent; border-top: 10rpx solid #666; }
效果:
參考連接:http://www.javashuo.com/article/p-qleixhvw-eg.html
下拉多選菜單
思路:用大view包小view,經過點擊狀態和 標誌記錄位來 打開(使下拉菜單顯示), 當選中其中一個或者再點
一次按鈕時,將會切換打開狀態或者關閉下拉菜單
核心:切換 ,記錄打開狀態位,經過點擊事件進行狀態位切換(這裏的狀態位爲下拉導航索引)。前端根據狀態位判斷是否渲染出來
data:{ //籌集項目列表 items:[], // 籌集項目類型 itemTypes:[], // 進度類型 processTypes:[], //排序類型 sortTypes:[ { id: 1, sortTypeName:"按目標金額從低到高"}, { id: 2, sortTypeName: "按目標金額從高到低"} ], // 選中的項目類型 selectItemType:-1, // 選中的進度類型 selectProcessType:-1, // 選中的排序類型 selectSortType:-1, // 顯示下拉菜單導航索引 showNavIndex:0, // 各種型菜單打開(滑動))狀態 itemTypeOpen:false, processOpen:false, sortOpen:false
這裏只寫其中的項目下拉菜單的顯示
下拉菜單觸發按鈕
<view class="nav-child" bindtap='listItemType' data-nav="1"> <view class='content'>項目類型</view> <view class="icon"></view> </view>
下拉菜單條目
<view class="itemTypeMenu {{itemTypeOpen? 'slidown':'slidup'}}" wx:if='{{showNavIndex==1}}' wx:for-index="index"> <view class="itemType" bindtap='selectItemType' data-itemTypeId='-1'> 不限 </view> <view class="itemType {{selectItemType==(index+1)?'highlight':''}}" wx:for="{{itemTypes}}" wx:for-item="itemType" wx:key="itemTypeId" data-itemTypeId="{{itemType.itemTypeId}}" bindtap='selectItemType' > {{itemType.itemTypeName}} </view> </view>
點擊事件的處理邏輯:
listItemType:function(e){ console.log(this.data.itemTypeOpen) // 若是已經打開了,再按一次就是關閉。 if (this.data.itemTypeOpen){ this.setData({ itemTypeOpen: false, showNavIndex:0 }) }else{ this.setData({ itemTypeOpen: true, //切換 要顯示的菜單導航 showNavIndex:e.currentTarget.dataset.nav }) } },
選中事件的處理邏輯:
selectItemType: function (e) { console.log(e.currentTarget) // 注意;這裏的data-傳過來的屬性會自動換成小寫 let id = e.currentTarget.dataset.itemtypeid; if (id == -1) { this.setData({ // 不限,也就是提交的這個篩選條件爲空 selectItemType: -1, itemTypeOpen: false, showNavIndex: 0 }) } else { this.setData({ selectItemType: id, itemTypeOpen: false, showNavIndex: 0 }) } let that = this; // 找出符合條件的items that.request_findItem(that); },
根據條件,查詢符合的項目
// 封裝一個根據條件查詢得請求函數 request_findItem:function(that){ wx.request({ url: baseUrl + 'item/findItems', data: { itemType: that.data.selectItemType, itemProcessType: that.data.selectProcessType, sortType: that.data.selectSortType }, method: "GET", success: function (res) { that.setData({ items: res.data.extend.items }) } }) }
總結:
選中高亮的實現原理:判斷Id與列表渲染的索引index是否匹配。若是匹配了就渲染高亮class
<view class="itemType {{selectItemType==(index+1)?'highlight':''}}" wx:for="{{itemTypes}}" wx:for-item="itemType" wx:key="itemTypeId" data-itemTypeId="{{itemType.itemTypeId}}" bindtap='selectItemType' >
不設置篩選條件,菜單條目爲 不限,經過設置爲-1來讓後端根據-1 作條件的判斷
@Override public List<RaiseItem> getItemsByType(FindItemDTO dto) { RaiseItemExample example = new RaiseItemExample(); RaiseItemExample.Criteria criteria = example.or(); Integer itemType = dto.getItemType(); Integer itemProcessType = dto.getItemProcessType(); if(itemType!=-1){ criteria.andItemTypeEqualTo(itemType); } if(itemProcessType!=-1){ criteria.andItemProcessTypeEqualTo(itemProcessType); } Integer sortType = dto.getSortType(); if(sortType!=-1){ if(sortType.equals(1)){ example.setOrderByClause("raise_target ASC"); }else if(sortType.equals(2)){ example.setOrderByClause("raise_target DESC"); } } return raiseItemMapper.selectByExample(example); }
效果演示:
參考連接:https://blog.csdn.net/u012927188/article/details/73369201/
思路 :
後端返回分頁數據
小程序:
加載更多組件
```
/* 上拉加載更多 */ .weui-loading { margin: 0 5px; width: 20px; height: 20px; display: inline-block; vertical-align: middle; -webkit-animation: weuiLoading 1s steps(12, end) infinite; animation: weuiLoading 1s steps(12, end) infinite; /* base64的格式 */ background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat; background-size: 100%; } .weui-loadmore { width: 65%; margin: 1.5em auto; line-height: 1.6em; font-size: 14px; text-align: center; } .weui-loadmore__tips { display: inline-block; vertical-align: middle; }
onReachBottom:function(){ let that = this; // 模擬延時,顯示加載更多 // wx.request({ // url: baseUrl+'', // }) // that.setData({ // }) let isLastPage = that.data.pageInfo.isLastPage; // 不是最後一頁,纔要請求分頁 if (!isLastPage){ setTimeout(() => { // 判斷一下這個觸底是常規觸底,仍是帶着條件的觸底事件 let f1 = that.data.selectItemType; let f2 = that.data.selectProcessType; let f3 = that.data.selectSortType; if (f1 != -1 || f2!=-1 || f3!=-1){ // 帶條件查詢 (其實帶條件和不帶條件其實在後端能夠合併爲一個接口的)) wx.request({ url: baseUrl + 'item/findItems', data: { itemType: that.data.selectItemType, itemProcessType: that.data.selectProcessType, sortType: that.data.selectSortType, pn: that.data.pageInfo.pageNum + 1 }, method: "GET", success: function (res) { let oldItems = that.data.items; let newItems = res.data.extend.pageInfo.list; let pageInfo = res.data.extend.pageInfo; // concat拼接後返回一個新的數組 newItems = oldItems.concat(newItems); that.setData({ pageInfo: pageInfo, items: newItems, isHideLoadMore: pageInfo.isLastPage ? true : false }) } }) }else{ // 不帶條件查詢 wx.request({ url: baseUrl + 'item/all', data: { pn: that.data.pageInfo.pageNum + 1 }, method: "GET", success: function (res) { let oldItems = that.data.items; let newItems = res.data.extend.pageInfo.list; // concat拼接後返回一個新的數組 newItems = oldItems.concat(newItems); let pageInfo = res.data.extend.pageInfo; that.setData({ pageInfo: pageInfo, items: newItems, isHideLoadMore: pageInfo.isLastPage ? true : false }) } }) } }, 1000); } }
後端返回分頁
// 獲取全部的項目基本信息 @RequestMapping(value = "/all",method = RequestMethod.GET) @ResponseBody public Msg getAllItems(@RequestParam(value = "pn",defaultValue = "1") Integer page_num){ PageHelper.startPage(page_num,5); List<RaiseItem> items = raiseItemService.getAllItems(); PageInfo<RaiseItem> pageInfo = new PageInfo<>(items); return Msg.success().add("pageInfo",pageInfo); }
// 根據類型來查詢符合的項目 @RequestMapping(value = "/findItems",method = RequestMethod.GET) @ResponseBody public Msg findItems(@RequestParam(value = "pn",defaultValue = "1") Integer page_num,FindItemDTO dto){ // 開始分頁 PageHelper.startPage(page_num,5); List<RaiseItem> items = raiseItemService.getItemsByType(dto); PageInfo<RaiseItem> pageInfo = new PageInfo<>(items); return Msg.success().add("pageInfo",pageInfo); }
效果:
加載完成後:
普通表單UI
文件(圖片)上傳UI
長按圖片刪除的操做
參考連接:https://www.jb51.net/article/140388.htm
參考JS語法:
splice() 方法向/從數組中添加/刪除項目,而後返回被刪除的項目。
註釋:該方法會改變原始數組。
arrayObject.splice(index,howmany,item1,.....,itemX)
參數 | 描述 |
---|---|
index | 必需。整數,規定添加/刪除項目的位置,使用負數可從數組結尾處規定位置。 |
howmany | 必需。要刪除的項目數量。若是設置爲 0,則不會刪除項目。 |
item1, ..., itemX | 可選。向數組添加的新項目。 |
類型 | 描述 |
---|---|
Array | 包含被刪除項目的新數組,若是有的話。 |
data: { Data: [{id:0,value:'a',name:'A' },{id:1,value:'b',name:'B' }], Index: 0, currentId },
對象數組的下拉菜單的使用
<picker class="picker" bindchange="bindChange" value="{{Index}}" range="{{Data}}" range-key="name"> <view > 當前選擇:{{Data[Index].name}} </view> </picker>
itemTypePickerChange:function(e){ this.setData({ Index:e.detail.value }) },
編寫基礎的wxml,wxss,js,獲取item表和detail表的基礎信息,佈局用flex嵌套flex合理佈局
效果以下:
這個按鈕懸浮固定在底部的實現:
```css
/* 按鈕固定高爲46px */
.detail_box{
margin-bottom: 92rpx;
}
/* 固定在底部,這樣能夠避免內容區的內容過多,讓按鈕一直看不到 */
.chou_button{
z-index: 999; position: fixed; bottom: 0; width: 100%; } ```
效果:
<!-- 籌集人的名單列表 --> <view class="raise_person_box"> <view class="raise_person_title"> 捐助人名單 </view> <view wx:for="{{persons}}" wx:for-key="person.item_person_id" wx:for-item="person" class="raise_person_item"> <view class="raise_person_item_left"> <view class="index_pic"> <image src="{{person.userAvatarUrl}}"></image> </view> </view> <view class="raise_person_item_right"> <view class="raise_person_item_right_top"> {{person.userNickName}} </view> <view class="raise_person_item_right_mid"> 支持了: <text class="mid_money">{{person.raiseMoney}} 元</text> </view> <view class="raise_person_item_right_bottom"> {{person.comment}} </view> <view class="raise_person_item_right_time"> {{person.raiseTime}} </view> </view> </view> </view>
總結:html頁面用了flex嵌套佈局吧
js部分:onShow()用於獲取捐助人名單+留言信息
wx.request({ url: baseUrl + 'item/person', data:{ itemId: that.data.itemId, }, method: "GET", success: function (res) { //調用 處理留言時間的函數,修改返回的數據 let list = res.data.extend.pageInfo.list; for (let i=0;i<list.length;i++){ let last_time = timeHandle(list[i].raiseTime) list[i].raiseTime = last_time; } that.setData({ persons: list, }) } })
此處要提的是一個特殊的經常使用需求:就是根據返回的時間戳計算出幾天前,幾個月前,又或者是具體的月份,年份
js部分:用了一個專門的函數放在單獨的js文件,放在utils目錄下,被其餘的js import引入使用
function commentTimeHandle(dateStr) { // dateStr = 2018-09-06 18:47:00" 測試時間 //獲取dataStr的秒數 打印結果--1536230820000 var publishTime = dateStr / 1000, date = new Date(publishTime * 1000), //獲取dateStr的標準格式 console.log(date) 打印結果 Thu Sep 06 2018 18:47:00 GMT+0800 (中國標準時間) // 獲取date 中的 年 月 日 時 分 秒 Y = date.getFullYear(), M = date.getMonth() + 1, D = date.getDate(), H = date.getHours(), m = date.getMinutes(), s = date.getSeconds(); // 對 月 日 時 分 秒 小於10時, 加0顯示 例如: 09-09 09:01 if (M < 10) { M = '0' + M; } if (D < 10) { D = '0' + D; } if (H < 10) { H = '0' + H; } if (m < 10) { m = '0' + m; } if (s < 10) { s = '0' + s; } // console.log("年", Y); // 年 2018 // console.log("月", M); // 月 09 // console.log("日", D); // 日 06 // console.log("時", H); // 時 18 // console.log("分", m); // 分 47 // console.log("秒", s); // 秒 00 //獲取此時此刻日期的秒數 var nowTime = new Date().getTime() / 1000, diffValue = nowTime - publishTime, // 獲取此時 秒數 與 要處理的日期秒數 之間的差值 // 一天86400秒 獲取相差的天數 取整 diff_days = parseInt(diffValue / 86400), // 一時3600秒 diff_hours = parseInt(diffValue / 3600), diff_minutes = parseInt(diffValue / 60), diff_secodes = parseInt(diffValue); if (diff_days > 0 && diff_days < 3) { //相差天數 0 < diff_days < 3 時, 直接返出 return diff_days + "天前"; } else if (diff_days <= 0 && diff_hours > 0) { return diff_hours + "小時前"; } else if (diff_hours <= 0 && diff_minutes > 0) { return diff_minutes + "分鐘前"; } else if (diff_secodes < 60) { if (diff_secodes <= 0) { return "剛剛"; } else { return diff_secodes + "秒前"; } } else if (diff_days >= 3 && diff_days < 30) { return M + '-' + D + ' ' + H + ':' + m; } else if (diff_days >= 30) { return Y + '-' + M + '-' + D + ' ' + H + ':' + m; } } module.exports = { timeHandle: commentTimeHandle }
如何使用:在JS裏引入這個JS文件的函數
import { timeHandle } from '../../utils/timehandle';
分頁後端:
// 獲取籌集人列表 @RequestMapping(value = "/person",method = RequestMethod.GET) @ResponseBody public Msg getPersons(@RequestParam(value = "pn",defaultValue = "1")Integer page_num, Integer itemId){ PageHelper.startPage(page_num,5); List<RaiseItemPerson> persons = raiseItemService.getRaisePersons(itemId); PageInfo<RaiseItemPerson> pageInfo = new PageInfo<>(persons); return Msg.success().add("pageInfo",pageInfo); }
總結:
獲取到用戶頭像,暱稱 (可在一個按鈕綁定兩個事件,一個用來獲取用戶信息,一個用來發出請求)
<button open-type='getUserInfo' type='primary' bindgetuserinfo="bindGetUserInfo" bindtap='donate'>我要幫幫他</button>
//獲取用戶信息 bindGetUserInfo: function (e) { console.log(e.detail.userInfo) this.setData({ userNickName: e.detail.userInfo.nickName, userAvatarUrl: e.detail.userInfo.avatarUrl }) }
效果:
總結:就是經過按鈕點擊切換模態框的顯示,而後在模態框裏模擬微信支付功能以及添加留言
<!-- modal支付模態框 --> <modal id="modal" hidden="{{hiddenmodal}}" title="支付頁面" confirm-text="肯定" cancel-text="取消" bindcancel="cancel" bindconfirm="confirm"> <text style="font-weight:bolder;font-size:35rpx">捐助金額:</text> <input type='text' placeholder="請填寫資助金額" class='weui-input' bindinput="bindKeyInput" auto-focus/> <text style="font-weight:bolder;font-size:35rpx">留言:</text> <input type='text' placeholder="留言" class='weui-input brief_description' bindinput="bindKeyComment"></input> </modal>
confirm:function(){ let openId = getApp().globalData.openId; console.log("openId: " + openId); let that = this; wx.request({ url: baseUrl+'item/donate', data:{ donate_money: that.data.donate_money, itemId: that.data.itemId, comment: that.data.comment, openId: openId, userNickName:that.data.userNickName, userAvatarUrl: that.data.userAvatarUrl }, method:"POST", header:{ "content-type": "application/x-www-form-urlencoded" }, success:function(res){ that.setData({ comment: "", donate_money: "", hiddenmodal: true, hiddenmodal:true }) // 發起請求 wx.request({ url: baseUrl + 'item/detail', data: { itemId: that.data.itemId }, success: function (res) { if (res.data.code == 200) { that.setData({ currentTarget: res.data.extend.detail.currentTarget, raisePersonNum: res.data.extend.detail.raisePersonNum, }) } } }) } }) }
cancel:function(){ this.setData({ donate_money:"", comment:"", hiddenmodal: true, }) }, //此處省略其餘input的處理事件
總結:
// 發送首頁圖片。對應首頁圖片的處理 wx.uploadFile({ url: baseUrl + 'item/imageIndex', filePath: files[0], name: 'img', header:{ 'content-type':'application/json' }, success: function (res) { console.log("res: "+res.data); // 微信小程序 uploadFile的坑是接收的是JSON字符串,不會幫你自動轉JS對象。因此須要本身解析data let data = JSON.parse(res.data); //獲取返回值 that.setData({ server_file_index: data.extend.file_index_path }) } })
點擊提交按鈕,圖片如何處理
分紅兩個接口,一個是首頁圖片,另外一個是詳情多個圖片urls.把文件名存放在數據庫中
add_submit:function(){ let that = this; let item_index = that.data.Index; // 先上傳圖片,後端處理成功後(經過返回值包含了首頁圖片路徑, //以及多個圖片展現的路徑)回調進行insert let files = that.data.files; // 發送首頁圖片。對應首頁圖片的處理 wx.uploadFile({ url: baseUrl + 'item/imageIndex', filePath: files[0], name: 'img', header:{ 'content-type':'application/json' }, success: function (res) { console.log("res: " + res.data); // 微信小程序 uploadFile的坑是接收的是JSON字符串,不會幫你自動轉JS對象。因此須要本身解析data let data = JSON.parse(res.data); //獲取返回值 that.setData({ server_file_index: data.extend.file_index_path }) //等server_file_index成功獲取後再執行下面的add操做 let i; //循環發送多個詳情的圖片 for (i = 1; i < files.length; i++) { // 採用閉包,保證索引值正確 (function (i) { //調用promise處理異步 that.GetImage(i, that).then((index) => { //最後一張處理完成 if (that.data.server_detail_files.length == (that.data.files.length - 1)) { console.log("開始執行提交add"); console.log("index: " + index); console.log("server_detail_file:" + that.data.server_detail_file); // 提交插入請求 wx.request({ url: baseUrl + '/item/add', method: 'POST', header: { "content-type": "application/x-www-form-urlencoded" }, data: { targetPerson: name, itemDescription: description, raiseTarget: money, itemType: that.data.itemTypes[that.data.Index].itemTypeId, createTime: date, description: detail_description, picIndexUrl: that.data.server_file_index, picDetailUrls: that.data.server_detail_files.join(',') }, success: function (res) { if (res.data.code == 200) { // 清空 that.setData({ targetPerson: "", itemDescription: "", raiseTarget: "", Index: 0, date: "", detail_description: "", server_file_index: "", server_detail_files: "", files: "" }) wx.switchTab({ url: '/pages/index/index', }) } } }) } }); })(i) } } }
基礎補習之閉包:
由於for循環,的索引index不會從1,2,3這樣,而是執行完了,顯示最後一個索引值。須要閉包控制一下。
演示:
文本測試:
18歲花季少女突發心臟病。急需救助!
小紅成績優異,家裏經濟貧困,在石鼓鎮。父母殘疾,只能在家裏下田。小紅下課後就回家作飯作菜給他們吃,本身暑假出去打工賺學費。學校老師說她的成績很是好,是年級前三的學生,模擬成績極可能考上211學校。
該案例已通過藍天志願組織實地考察,經多名志願者覈實,狀況屬實。但願你們能給予幫助,奉獻大愛。
1. 微信小程序 uploadFile的坑是接收的是JSON字符串,不會幫你自動轉JS對象。因此須要本身解析data
for循環裏有異步請求,想要for裏面的異步請求都執行完再執行其餘的怎麼作?
異步請求:
// promise GetImage:function(i,that){ console.log("當前循環:"+i); return new Promise(function (resolve, reject) { wx.uploadFile({ url: baseUrl + '/item/images', filePath: that.data.files[i], name: 'img', success: (res) => { // console.log("這是第"+i+"次循環") console.log(that.data); //先拿到舊的 var server_detail_files = that.data.server_detail_files; console.log("server_detail_files" + server_detail_files); //服務端返回的 let data = JSON.parse(res.data); let files_detail_path = data.extend.files_detail_path; console.log("files_detail_path:" + files_detail_path) //若是是拼的第一個,加入數組 console.log("server_detail_files:" + server_detail_files) //push是在原數組上操做,並返回新的長度。 server_detail_files.push(files_detail_path); //獲取返回值 that.setData({ server_detail_files: server_detail_files }) resolve(server_detail_files); } }) }) }
for循環裏異步,而且經過判斷i==要執行下一步的值去執行add請求
//循環發送多個詳情的圖片 for (i = 1; i < files.length; i++) { // 採用閉包,保證索引值正確 (function (i) { //調用promise處理異步 that.GetImage(i,that).then(()=>{ //最後一張處理完成 console.log("i: "+i); //在then裏判斷是不是最後一張圖片,從而達到完成全部的for循環後再執行這個提交插入的請求 if (that.data.server_detail_files.length == (that.data.files.length-1)) { // 提交插入請求 wx.request({ url: baseUrl + '/item/add', method: 'POST', header:{ "content-type":"application/x-www-form-urlencoded" }, data: { targetPerson: name, itemDescription: description, raiseTarget: money, itemType: that.data.itemTypes[that.data.Index].itemTypeId, createTime: date, description: detail_description, picIndexUrl: that.data.server_file_index, picDetailUrls: that.data.server_detail_files.join(',') }, success: function (res) { if (res.code == 200) { // 清空 that.setData({ targetPerson: "", itemDescription: "", raiseTarget: "", Index: 0, date: "", detail_description: "", server_file_index: "", server_detail_files: "", files: "" }) wx.navigateTo({ url: 'pages/index/index', }) return; } } }) } }); })(i) }
若是微信小程序使用post請求,後端沒數據的話,說明小程序沒有設置header爲
header: { 'content-type': 'application/json' },
PowerDesigner的使用
安裝: https://blog.csdn.net/sinat_34104446/article/details/79885141
sys_admin:
id | username | password | salt |
---|---|---|---|
1 | zhanp | @gd5@a6#ca1f5b@30@3a@2bcc#5F0b0f40@f@5a6@1!a4a5b6b0F1#b1!0a1cfa2 | d4171b48-fca9-45b1-9bb7-716ea057aa25 |
raise_item:
item_id | target_person | raise_target | current_target | raise_person_num | pic_index_url | item_description | item_type_id | item_process_type_id |
---|---|---|---|---|---|---|---|---|
1 | 小江 | 5000 | 1000 | 60 | http://localhost/image/po1.jpg | xxxx加油,打敗病魔 | 1 | 1 |
2 | 小洋 | 6000 | 2000 | 70 | http://localhost/image/po2.jpg | xxx加油,努力讀書 | 2 | 2 |
item_process_type
item_process_type_id | item_process_type_name |
---|---|
1 | 未完成 |
2 | 即將完成 |
3 | 已完成 |
4 | 已結束 |
(已結束是時間已過,該項目取消籌款了)
raise_item_type
item_type_id | item_type_name |
---|---|
1 | 孤寡老人 |
2 | 貧困學生 |
3 | 留守兒童 |
4 | 患病在身 |
5 | 其餘 |
raise_item_detail
item_detail_id | item_id | description | pic_detail_urls | create_time |
---|---|---|---|---|
raise_item_process (一個項目能夠有屢次進展)
item_process_id | item_id | pic_process_urls | description | |
---|---|---|---|---|
1 | 1 | |||
2 | 1 |
raise_item_person
item_person_id | item_id | user_avatar_url | user_nick_name | raise_money | comment | raise_time | open_id |
---|---|---|---|---|---|---|---|
後續還有排行榜
單元測試模擬數據的過程當中遇到的bug
<!--在spring單元測試中,因爲引入validator而致使的Tomcat7及如下的EL表達式版本不一致--> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-el-api</artifactId> <version>8.5.24</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jasper-el</artifactId> <version>8.5.24</version> <scope>provided</scope> </dependency>
報錯:
解決方法:由於@ResponseBody,可是底層的jackson忘記引入了
<!--jackson支持--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency>
<context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath*:log/log4j.properties</param-value> </context-param> <listener> <description>log4j</description> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener>
必定要仔細看報錯的部分,會顯示哪一行報錯,不要本身瞎找。否則改一天你都不知道哪裏錯。
指示add.js 221行錯了
測試:
爲何服務器端的mysql一直連不上去?
由於root只容許localhost訪問,因此要修改。
別忘了flush一下
成功:
今天在服務器安裝mysql
以後,登陸發現密碼錯誤,可是我沒有設置密碼呀,最後百度以後得知,mysql
在5.7版本以後會自動建立一個初始密碼。
報錯以下:
[root@mytestlnx02 ~]# mysql -u root -p Enter password: ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
mysql
服務是否啓動,若是啓動,關閉mysql
服務//查看mysql服務狀態 [root@mytestlnx02 ~]# ps -ef | grep -i mysql root 22972 1 0 14:18 pts/0 00:00:00 /bin/sh /usr/bin/mysqld_safe --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock --pid-file=/var/run/mysqld/mysqld.pid --basedir=/usr --user=mysql mysql 23166 22972 0 14:18 pts/0 00:00:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --log-error=/var/log/mysqld.log --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/lib/mysql/mysql.sock root 23237 21825 0 14:22 pts/0 00:00:00 grep -i mysql //關閉服務 [root@mytestlnx02 ~]# service mysql stop [root@mytestlnx02 ~]#
mysql
的配置文件my.cnf
my.cnf`配置文件的位置,通常在`/etc/my.cnf`,有些版本在`/etc/mysql/my.cnf
在配置文件中,增長2行代碼
[mysqld] skip-grant-tables
做用是登陸mysql
的時候跳過密碼驗證
而後啓動mysql
服務,並進入mysql
[root@mytestlnx02 ~]# service mysqld start [root@mytestlnx02 ~]# [root@mytestlnx02 ~]# mysql -u root Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
鏈接mysql
這個數據庫,修改用戶密碼
mysql> use mysql; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> update mysql.user set authentication_string=password('root_password') where user='root'; Query OK, 1 row affected, 1 warning (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 1 mysql> flush privileges; Query OK, 0 rows affected (0.00 sec) mysql> exit
mysql
服務先將以前加在配置文件裏面的2句代碼註釋或刪除掉,而後重啓mysql
服務,就可使用剛剛設置的密碼登陸了。
[root@mytestlnx02 ~]# service mysql start [root@mytestlnx02 ~]# [root@mytestlnx02 ~]# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g.
在CentOS
上的操做方式有所不一樣。
執行修改密碼的命令一直報錯
mysql> update user set authentication_string=password('xxxxxxxx') where User='root'; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '('root_password') where User='root'' at line 1
不多是語法問題,檢查了不少遍,最後發現CentOS
下應該這樣操做:
[root@VM_0_8_centos ~]# grep 'temporary password' /var/log/mysqld.log 2018-09-26T04:25:54.927944Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: DN34N/=?aIfZ
能夠看到初始密碼爲DN34N/=?aIfZ
[root@VM_0_8_centos ~]# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 8 Server version: 8.0.12 MySQL Community Server - GPL Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
mysql> ALTER USER 'root' IDENTIFIED BY 'xxxxxxxxx'; ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement. mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'xxxxxxxx'; Query OK, 0 rows affected (0.11 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec) mysql> exit Bye
[root@VM_0_8_centos ~]# service mysqld stop Redirecting to /bin/systemctl stop mysqld.service [root@VM_0_8_centos ~]# service mysqld start Redirecting to /bin/systemctl start mysqld.service
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://xxxx/sky_chou?useUnicode=true&characterEncoding=UTF-8 jdbc.username=root jdbc.password=xxxxx
記住:必定不要添加useSSL=true這種配置信息,否則會SQL報錯
改爲localhost,否則不會識別服務器的ip。!
經過nginx升級到HTTPS
修改nginx.conf文件
#HTTPS server server { listen 443; server_name 你的域名; ssl on; ssl_certificate xxxx_bundle.crt; ssl_certificate_key xxx.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; ssl_prefer_server_ciphers on; location / { client_max_body_size 16m; client_body_buffer_size 128k; proxy_pass http://127.0.0.1:9999/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_next_upstream off; proxy_connect_timeout 30; proxy_read_timeout 300; proxy_send_timeout 300; } }
要着重修改的SSL相關地方:
ssl on; ssl_certificate xxxxx.crt; ssl_certificate_key xxxx.key;
這些是網上的固定配置
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_next_upstream off; proxy_connect_timeout 30; proxy_read_timeout 300; proxy_send_timeout 300;
nginx代理443端口的配置部分
server { listen 443; server_name 你的域名; location / { client_max_body_size 16m; client_body_buffer_size 128k; proxy_pass http://127.0.0.1:9999/;
記住全部的server模塊的配置都應該包含在http塊裏面,否則會報錯的!
成功標誌:
4.保存退出。
5.再輸入:source /etc/profilecond tomcat在生效
6.第一個tomcat,保持解壓後的原狀不用修改,
來到第二個tomcat的bin目錄下打開catalina.sh ,找到下面紅字,
# OS specific support. $var must be set to either true or false.
在下面增長以下代碼
export CATALINA_BASE=$CATALINA_2_BASE
export CATALINA_HOME=$CATALINA_2_HOME
7.來到第二個tomcat的conf目錄下
打開server.xml更改端口:
修改server.xml配置和第一個不一樣的啓動、關閉監聽端口。
修改後示例以下:
<Connector port="9080" maxHttpHeaderSize="8192" 端口:8080->9080
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />
<Connector port="9009" 端口:8009->9009
enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />
8.分別進入兩個tomcat的bin目錄,啓動tomcat--./startup.sh
9.而後訪問http://localhost:8080 和 http://localhost:9080 均可以看到熟悉的tomcat歡迎界面