App界面設計對于設計師而言一直是盛久不衰的話題,尤其是如今越來越多的流量轉移到了移動平臺,使得更多的UI設計師涌入移動端領域,甚至出現了市場飽和的言論,對于從事移動端的UI設計師來講,充滿壓力的同時又面臨無限機遇,唯有不斷的學習才能滋生出源源不斷的設計靈感,站穩腳跟。
摹客想在這方面給各位設計師朋友做點什么,除了提供簡單好用的設計工具,我們也整理了非常多的優秀設計案例,希望可以對設計師朋友有借鑒意義。這將會是一個系列的專題,我們以月為單位,整理了國內外設計師的優秀APP界面設計案例,我們是搬運工,更是好設計的傳達者,希望你會喜歡。
接下來為大家分享精美的app UI設計案例:
--手機appUI設計--
更多精彩文章:
本文從零開始,逐步講解如何用react全家桶搭建一個完整的react項目。文中針對react、webpack、babel、react-route、redux、redux-saga的核心配置會加以講解,通過這個項目,可以系統的了解react技術棧的主要知識,避免搭建一次后面就忘記的情況。
代碼庫:https://github.com/teapot-py/react-demo
首先關于主要的npm包版本列一下:
思考一下webpack到底做了什么事情?其實簡單來說,就是從入口文件開始,不斷尋找依賴,同時為了解析各種不同的文件加載相應的loader,最后生成我們希望的類型的目標文件。
這個過程就像是在一個迷宮里尋寶,我們從入口進入,同時我們也會不斷的接收到下一處寶藏的提示信息,我們對信息進行解碼,而解碼的時候可能需要一些工具,比如說鑰匙,而loader就像是這樣的鑰匙,然后得到我們可以識別的內容。
回到我們的項目,首先進行項目的初始化,分別執行如下命令
mkdir react-demo // 新建項目文件夾
cd react-demo // cd到項目目錄下
npm init // npm初始化
引入webpack
npm i webpack --save
touch webpack.config.js
對webpack進行簡單配置,更新webpack.config.js
const path = require('path');
module.exports = {
entry: './app.js', // 入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 定義輸出目錄
filename: 'my-first-webpack.bundle.js' // 定義輸出文件名稱
}
};
更新package.json文件,在scripts中添加webpack執行命令
"scripts": {
"dev": "./node_modules/.bin/webpack --config webpack.config.js"
}
如果有報錯請按提示安裝webpack-cli
npm i webpack-cli
執行webpack
npm run dev
如果在項目文件夾下生成了dist文件,說明我們的配置是沒有問題的。
安裝react相關包
npm install react react-dom --save
更新app.js入口文件
import React from 'react
import ReactDom from 'react-dom';
import App from './src/views/App';
ReactDom.render(<App />, document.getElementById('root'));
創建目錄 src/views/App,在App目錄下,新建index.js文件作為App組件,index.js文件內容如下:
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (<div>App Container</div>);
}
}
export default App;
在根目錄下創建模板文件index.html
<!DOCTYPE html>
<html>
<head>
<title>index</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
</head>
<body>
<div id="root"></div>
</body>
</html>
到了這一步其實關于react的引入就OK了,不過目前還有很多問題沒有解決
Babel是一個工具鏈,主要用于在舊的瀏覽器或環境中將ECMAScript2015+的代碼轉換為向后兼容版本的JavaScript代碼。
安裝babel-loader,@babel/core,@babel/preset-env,@babel/preset-react
npm i babel-loader@8 @babel/core @babel/preset-env @babel/preset-react -D
更新webpack.config.js
module: {
rules: [
{
test: /\.js$/, // 匹配.js文件
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
根目錄下創建并配置.babelrc文件
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
配置HtmlWebPackPlugin
這個插件最主要的作用是將js代碼通過
npm i html-webpack-plugin -D
webpack新增HtmlWebPackPlugin配置
至此,我們看一下webpack.config.js文件的完整結構
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-first-webpack.bundle.js'
},
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: './index.html',
filename: path.resolve(__dirname, 'dist/index.html')
})
]
};
執行 npm run start,生成 dist文件夾
當前目錄結構如下
可以看到在dist文件加下生成了index.html文件,我們在瀏覽器中打開文件即可看到App組件內容。
webpack-dev-server可以極大的提高我們的開發效率,通過監聽文件變化,自動更新頁面
安裝 webpack-dev-server 作為 dev 依賴項
npm i webpack-dev-server -D
更新package.json的啟動腳本
“dev": "webpack-dev-server --config webpack.config.js --open"
webpack.config.js新增devServer配置
devServer: {
hot: true, // 熱替換
contentBase: path.join(__dirname, 'dist'), // server文件的根目錄
compress: true, // 開啟gzip
port: 8080, // 端口
},
plugins: [
new webpack.HotModuleReplacementPlugin(), // HMR允許在運行時更新各種模塊,而無需進行完全刷新
new HtmlWebPackPlugin({
template: './index.html',
filename: path.resolve(__dirname, 'dist/index.html')
})
]
redux是用于前端數據管理的包,避免因項目過大前端數據無法管理的問題,同時通過單項數據流管理前端的數據狀態。
創建多個目錄
下面我們來通過redux實現一個計數器的功能
安裝依賴
npm i redux react-redux -D
在actions文件夾下創建index.js文件
export const increment = () => {
return {
type: 'INCREMENT',
};
};
在reducers文件夾下創建index.js文件
const initialState = {
number: 0
};
const incrementReducer = (state = initialState, action) => {
switch(action.type) {
case 'INCREMENT': {
state.number += 1
return { ...state }
break
};
default: return state;
}
};
export default incrementReducer;
更新store.js
import { createStore } from 'redux';
import incrementReducer from './reducers/index';
const store = createStore(incrementReducer);
export default store;
更新入口文件app.js
import App from './src/views/App';
import ReactDom from 'react-dom';
import React from 'react';
import store from './src/store';
import { Provider } from 'react-redux';
ReactDom.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
更新App組件
import React from 'react';
import { connect } from 'react-redux';
import { increment } from '../../actions/index';
class App extends React.Component {
constructor(props) {
super(props);
}
onClick() {
this.props.dispatch(increment())
}
render() {
return (
<div>
<div>current number: {this.props.number} <button onClick={()=>this.onClick()}>點擊+1</button></div>
</div>
);
}
}
export default connect(
state => ({
number: state.number
})
)(App);
點擊旁邊的數字會不斷地+1
redux-saga通過監聽action來執行有副作用的task,以保持action的簡潔性。引入了sagas的機制和generator的特性,讓redux-saga非常方便地處理復雜異步問題。
redux-saga的原理其實說起來也很簡單,通過劫持異步action,在redux-saga中進行異步操作,異步結束后將結果傳給另外的action。
下面就接著我們計數器的例子,來實現一個異步的+1操作。
安裝依賴包
npm i redux-saga -D
新建src/sagas/index.js文件
import { delay } from 'redux-saga'
import { put, takeEvery } from 'redux-saga/effects'
export function* incrementAsync() {
yield delay(2000)
yield put({ type: 'INCREMENT' })
}
export function* watchIncrementAsync() {
yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}
解釋下所做的事情,將watchIncrementAsync理解為一個saga,在這個saga中監聽了名為INCREMENT_ASYNC的action,當INCREMENT_ASYNC被dispatch時,會調用incrementAsync方法,在該方法中做了異步操作,然后將結果傳給名為INCREMENT的action進而更新store。
更新store.js
在store中加入redux-saga中間件
import { createStore, applyMiddleware } from 'redux';
import incrementReducer from './reducers/index';
import createSagaMiddleware from 'redux-saga'
import { watchIncrementAsync } from './sagas/index'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(incrementReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(watchIncrementAsync)
export default store;
更新App組件
在頁面中新增異步提交按鈕,觀察異步結果
import React from 'react';
import { connect } from 'react-redux';
import { increment } from '../../actions/index';
class App extends React.Component {
constructor(props) {
super(props);
}
onClick() {
this.props.dispatch(increment())
}
onClick2() {
this.props.dispatch({ type: 'INCREMENT_ASYNC' })
}
render() {
return (
<div>
<div>current number: {this.props.number} <button onClick={()=>this.onClick()}>點擊+1</button></div>
<div>current number: {this.props.number} <button onClick={()=>this.onClick2()}>點擊2秒后+1</button></div>
</div>
);
}
}
export default connect(
state => ({
number: state.number
})
)(App);
觀察結果我們會發現如下報錯:
這是因為在redux-saga中用到了Generator函數,以我們目前的babel配置來說并不支持解析generator,需要安裝@babel/plugin-transform-runtime
npm install --save-dev @babel/plugin-transform-runtime
這里關于babel-polyfill、和transfor-runtime做進一步解釋
Babel默認只轉換新的JavaScript語法,而不轉換新的API。例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉譯。如果想使用這些新的對象和方法,必須使用 babel-polyfill,為當前環境提供一個墊片。
Babel轉譯后的代碼要實現源代碼同樣的功能需要借助一些幫助函數,而這些幫助函數可能會重復出現在一些模塊里,導致編譯后的代碼體積變大。
Babel 為了解決這個問題,提供了單獨的包babel-runtime供編譯模塊復用工具函數。
在沒有使用babel-runtime之前,庫和工具包一般不會直接引入 polyfill。否則像Promise這樣的全局對象會污染全局命名空間,這就要求庫的使用者自己提供 polyfill。這些 polyfill一般在庫和工具的使用說明中會提到,比如很多庫都會有要求提供 es5的polyfill。
在使用babel-runtime后,庫和工具只要在 package.json中增加依賴babel-runtime,交給babel-runtime去引入 polyfill 就行了;
詳細解釋可以參考
Babel插件一般盡可能拆成小的力度,開發者可以按需引進。比如對ES6轉ES5的功能,Babel官方拆成了20+個插件。
這樣的好處顯而易見,既提高了性能,也提高了擴展性。比如開發者想要體驗ES6的箭頭函數特性,那他只需要引入transform-es2015-arrow-functions插件就可以,而不是加載ES6全家桶。
但很多時候,逐個插件引入的效率比較低下。比如在項目開發中,開發者想要將所有ES6的代碼轉成ES5,插件逐個引入的方式令人抓狂,不單費力,而且容易出錯。
這個時候,可以采用Babel Preset。
可以簡單的把Babel Preset視為Babel Plugin的集合。比如babel-preset-es2015就包含了所有跟ES6轉換有關的插件。
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
點擊按鈕會在2秒后執行+1操作。
在web應用開發中,路由系統是不可或缺的一部分。在瀏覽器當前的URL發生變化時,路由系統會做出一些響應,用來保證用戶界面與URL的同步。隨著單頁應用時代的到來,為之服務的前端路由系統也相繼出現了。而react-route則是與react相匹配的前端路由。
引入react-router-dom
npm install --save react-router-dom -D
更新app.js入口文件增加路由匹配規則
import App from './src/views/App';
import ReactDom from 'react-dom';
import React from 'react';
import store from './src/store';
import { Provider } from 'react-redux';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
const About = () => <h2>頁面一</h2>;
const Users = () => <h2>頁面二</h2>;
ReactDom.render(
<Provider store={store}>
<Router>
<Switch>
<Route path="/" exact component={App} />
<Route path="/about/" component={About} />
<Route path="/users/" component={Users} />
</Switch>
</Router>
</Provider>
, document.getElementById('root'));
更新App組件,展示路由效果
import React from 'react';
import { connect } from 'react-redux';
import { increment } from '../../actions/index';
import { Link } from "react-router-dom";
class App extends React.Component {
constructor(props) {
super(props);
}
onClick() {
this.props.dispatch(increment())
}
onClick2() {
this.props.dispatch({ type: 'INCREMENT_ASYNC' })
}
render() {
return (
<div>
<div>react-router 測試</div>
<nav>
<ul>
<li>
<Link to="/about/">頁面一</Link>
</li>
<li>
<Link to="/users/">頁面二</Link>
</li>
</ul>
</nav>
<br/>
<div>redux & redux-saga測試</div>
<div>current number: {this.props.number} <button onClick={()=>this.onClick()}>點擊+1</button></div>
<div>current number: {this.props.number} <button onClick={()=>this.onClick2()}>點擊2秒后+1</button></div>
</div>
);
}
}
export default connect(
state => ({
number: state.number
})
)(App);
點擊列表可以跳轉相關路由
至此,我們已經一步步的,完成了一個簡單但是功能齊全的react項目的搭建,下面回顧一下我們做的工作
麻雀雖小,五臟俱全,希望通過最簡單的代碼快速的理解react工具鏈。其實這個小項目中還是很多不完善的地方,比如說樣式的解析、Eslint檢查、生產環境配置,雖然這幾項是一個完整項目不可缺少的部分,但是就demo項目來說,對我們理解react工具鏈可能會有些干擾,所以就不在項目中加了。
后面會新建一個分支,把這些完整的功能都加上,同時也會對當前的目錄結構進行優化。
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
部分借鑒自:csdn 作者:鄭清
原文鏈接:
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
在與客戶的溝通中,聆聽了許多建議,學習到了很多知識,也收到過贊美與批評,在經過千錘百煉過后 總結了一點點經驗
首先:
框架選擇
網站css框架選擇(簡潔,節約成本,快速開發)
對于一些簡單靜態網站,展示類網站項目,達到快速開發建站,而又節約成本人力的情況下 選擇一些用于css庫的框架最為合適,
1.Bootstrap
Bootstrap 是最受歡迎的 HTML、CSS 和 JS 框架,用于開發響應式布局、移動設備優先的 WEB 項目。
預處理工具 雖然可以直接使用 Bootstrap 提供的 CSS 樣式表,但是不要忘記,Bootstrap 的源碼是采用最流行的 CSS 預處理工具
一個框架、多種設備。 你的網站和應用能在 Bootstrap 的幫助下通過同一份源碼快速、有效地適配手機、平板和 PC 設備,這一切都是 CSS 媒體 查詢(Media Query)的功勞。
功能完備 Bootstrap 提供了全面、美觀的文檔,你能在這里找到關于普通 HTML 元素、HTML 和 CSS 組件以及 jQuery 插件方面的所有詳細文檔。
Bootstrap 是最受歡迎的 CSS 框架,被認為是擁有最好的響應性的CSS框架。專為前端開發而設計,有助于構建web設計理念、移動優先項目、網格系統、排版和按鈕等。
2.layui
開源模塊化前端 UI 框架
開源模塊化前端 UI 框架
由職業前端傾情打造,面向全層次的前后端開發者,易上手開箱即用的 Web UI 組件庫
Layui 是一款采用自身模塊規范編寫的情懷型前端UI框架,遵循原生HTML/CSS/JS的書寫與組織形式,門檻極低,拿來即用。其外在極簡,卻又不失飽滿的內在,體積輕盈,組件豐盈,從核心代碼到API的每一處細節都經過精心雕琢,非常適合界面的快速開發。
3.Semantic-UI
Semantic 是一個開發框架,可以使用人性化的 HTML 幫助創建漂亮的響應式布局。Semantic UI 旨在使網站構建過程更加語義化。核心特征是利用自然語言原理使代碼更易于閱讀,更容易理解。
4.Pure
Pure 非常輕量級,經過壓縮后不過 3.8KB。這是一個特別為移動端考慮的框架,為了壓縮大小,每一行代碼都經過仔細考量。當然如果你不使用框架給出的全部模塊,體量還可以更小。
5.Skeleton
Skeleton 如其名字,非常小巧,設計簡約,麻雀雖小五臟俱全。網格系統,文本,表單,按鈕,列表,表格,媒體查詢等功能面面俱到。非常適合快速創建簡約網站的需求。
快速搭建
為客戶節省時間成本, 并滿足客戶快速建站的需求,開發過程中 使用到css模塊化,html也應簡潔實用。
在我們最初學習寫頁面的時候,大家都學過怎么去寫 css,也就以下幾種情況:
我們在不斷摸索中,逐漸形成了以編寫內嵌樣式和外部樣式為主要的編寫習慣。
讀到這里大家肯定有所疑問,為什么不建議使用行內樣式?
使用行內樣式的缺點
然后我們繼續剖析一下,為什么不建議使用導入樣式?
經測試,在 css 中使用 @import 會有以下兩種情況:
1、在 IE6-8 下,@import 聲明指向的樣式表并不會與頁面其他資源并發加載,而是等頁面所有資源加載完成后才開始下載。
2、如果在 link 標簽中去 @import 其他 css,頁面會等到所有資源加載完成后,才開始解析 link 標簽中 @import 的 css。
使用導入樣式的缺點 - 導入樣式,只能放在 style 標簽的第一行,放其他行則會無效。 - @import 聲明的樣式表不能充分利用瀏覽器并發請求資源的行為,其加載行為往往會延后觸發或被其他資源加載掛起。 - 由于 @import 樣式表的延后加載,可能會導致頁面樣式閃爍。
隨著時間的不斷發展,我們逐漸發現,編寫源生的 css 其實并不友好,例如:源生的 css 不支持變量,不支持嵌套,不支持父選擇器等等,這些種種問題,催生出了像 sass/less 這樣的預處理器。
預處理器主要是強化了 css 的語法,彌補了上文說了這些問題,但本質上,打包出來的結果和源生的 css 都是一樣的,只是對開發者友好,寫起來更順滑。
隨著前端工程化的不斷發展,越來越多的工具被前端大佬們開發出來,愿景是把所有的重復性的工作都交給機器去做,在 css 領域就產生了 postcss。
postcss 可以稱作為 css 界的 babel,它的實現原理是通過 ast 去分析我們的 css 代碼,然后將分析的結果進行處理,從而衍生出了許多種處理 css 的使用場景。
常用的 postcss 使用場景有:
隨著 react、vue 等基于模塊化的框架的普及使用,我們編寫源生 css 的機會也越來越少。我們常常將頁面拆分成許多個小組件,然后像搭積木一樣將多個小組件組成最終呈現的頁面。
但是我們知道,css 是根據類名去匹配元素的,如果有兩個組件使用了一個相同的類名,后者就會把前者的樣式給覆蓋掉,看來解決樣式命名的沖突是個大問題。
為了解決這個問題,產生出了 CSS 模塊化的概念。
你如果遇到如上問題,那么就很有必要使用 css 模塊化。
BEM 的意思就是塊(block)、元素(element)、修飾符(modifier)。是由 Yandex 團隊提出的一種前端命名方法論。這種巧妙的命名方法讓你的 css 類對其他開發者來說更加透明而且更有意義。
BEM 的命名規范如下:
/* 塊即是通常所說的 Web 應用開發中的組件或模塊。每個塊在邏輯上和功能上都是相互獨立的。 */ .block { } /* 元素是塊中的組成部分。元素不能離開塊來使用。BEM 不推薦在元素中嵌套其他元素。 */ .block__element { } /* 修飾符用來定義塊或元素的外觀和行為。同樣的塊在應用不同的修飾符之后,會有不同的外觀 */ .block--modifier { }
通過 bem 的命名方式,可以讓我們的 css 代碼層次結構清晰,通過嚴格的命名也可以解決命名沖突的問題,但也不能完全避免,畢竟只是一個命名約束,不按規范寫照樣能運行。
CSS Modules 指的是我們像 import js 一樣去引入我們的 css 代碼,代碼中的每一個類名都是引入對象的一個屬性,通過這種方式,即可在使用時明確指定所引用的 css 樣式。
并且 CSS Modules 在打包的時候會自動將類名轉換成 hash 值,完全杜絕 css 類名沖突的問題。
使用方式如下:
1、定義 css 文件。
.className { color: green; } /* 編寫全局樣式 */ :global(.className) { color: red; } /* 樣式復用 */ .otherClassName { composes: className; color: yellow; } .otherClassName { composes: className from "./style.css"; }
2、在 js 模塊中導入 css 文件。
import styles from "./style.css"; element.innerHTML = '<div class="' + styles.className + '">';
3、配置 css-loader 打包。
CSS Modules 不能直接使用,而是需要進行打包,一般通過配置 css-loader 中的 modules 屬性即可完成 css modules 的配置。
// webpack.config.js module.exports = { module: { rules: [ { test: /\.css$/, use:{ loader: 'css-loader', options: { modules: { // 自定義 hash 名稱 localIdentName: '[path][name]__[local]--[hash:base64:5]', } } } ] } };
4、最終打包出來的 css 類名就是由一長串 hash 值生成。
._2DHwuiHWMnKTOYG45T0x34 { color: red; } ._10B-buq6_BEOTOl9urIjf8 { background-color: blue; }
CSS in JS,意思就是使用 js 語言寫 css,完全不需要些單獨的 css 文件,所有的 css 代碼全部放在組件內部,以實現 css 的模塊化。
CSS in JS 其實是一種編寫思想,目前已經有超過 40 多種方案的實現,最出名的是 styled-components。
使用方式如下:
import React from "react"; import styled from "styled-components"; // 創建一個帶樣式的 h1 標簽 const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; `; // 創建一個帶樣式的 section 標簽 const Wrapper = styled.section` padding: 4em; background: papayawhip; `; // 通過屬性動態定義樣式 const Button = styled.button` background: ${props => (props.primary ? "palevioletred" : "white")}; color: ${props => (props.primary ? "white" : "palevioletred")}; font-size: 1em; margin: 1em; padding: 0.25em 1em; border: 2px solid palevioletred; border-radius: 3px; `; // 樣式復用 const TomatoButton = styled(Button)` color: tomato; border-color: tomato; `; <Wrapper> <Title>Hello World, this is my first styled component!</Title> <Button primary>Primary</Button> </Wrapper>;
可以看到,我們直接在 js 中編寫 css,案例中在定義源生 html 時就創建好了樣式,在使用的時候就可以渲染出帶樣式的組件了。
除此之外,還有其他比較出名的庫:
最后放一張總結好的圖。
下一篇我們講一下主流js框架 與js開發
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
部分借鑒自:知乎 作者:孟思行
原文鏈接:
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
Bootstrap 是最受歡迎的 HTML、CSS 和 JS 框架,用于開發響應式布局、移動設備優先的 WEB 項目。
Bootstrap 是最受歡迎的 HTML、CSS 和 JS 框架,用于開發響應式布局、移動設備優先的 WEB 項目。
Bootstrap 是最受歡迎的 HTML、CSS 和 JS 框架,用于開發響應式布局、移動設備優先的 WEB 項目。
即時通訊界面設計 表達其物流行業的專業性和商務性,標識整體精致細膩,令人印象深刻,在界面設計時以厚重,大氣的配色方案和視覺風格提升整個品牌的含義。
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
現在是互聯網逐漸發展,已經出現了很多可以供自己寫博客的網站,大家可以在上面 發表自己的文章,供自己記錄或者是供他人閱讀。但是,可不可以自己搭建一個只屬于自己的個人博客網站呢?這篇文章就帶你從0開始搭建一個自己的個人博客網站,并部署到屬于自己服務器。這里有一點要說的是,沒有服務器的同學使用自己機器的linux系統也是一樣的操作。我們選用一個很好用的博客框架Hexo進行搭建我們的個人博客。
Hexo是一個快速,簡介而且高效的博客框架,Hexo 使用Markdown(或其他渲染引擎)解析文章,在幾秒內,即可生成一個靜態網頁展示我們發布的文章,同時也提供了大量精美的博客主題供我們使用。
我們使用Centos7系統作為演示,使用其他linux系統也是可以的,只需要更換為對應Linux版本的軟件安裝命令即可。
1.安裝Git
直接使用yum安裝即可,在命令行輸入 yum -y install git
完成之后輸入git version 查看是否安裝成功,如果顯示git版本信息即為成功,如下:
2.安裝Node.js
Node.js是一種運行在服務端的JavaScript,是一個基于Chrome JavaScript運行時建立的一個平臺。
Hexo基于Node.js,所以安裝Node.js是必須的一個操作,安裝步驟如下:
2.1:下載安裝包:
wget https://nodejs.org/dist/v12.13.1/node-v12.13.1-linux-x64.tar.xz
2.2:解壓縮軟件包并配置環境變量:
#解壓 tar -xvJf node-v6.10.1-linux-x64.tar.xz #移動到/usl/lcoal目錄下 mv node-v6.10.1-linux-x64 /usr/local/node-v6 #創建軟鏈接 ln -s /usr/local/node-v6/bin/node /bin/node ln -s /usr/local/node-v6/bin/npm /bin/npm #添加環境變量 echo 'export PATH=/usr/local/node-v6/bin:$PATH' >> /etc/profile source /etc/profile #讓環境變量生效
2.3:測試是否安裝成功:
在命令行輸入node -v 和 npm -v,若是顯示出了版本號,即為安裝成功:
3.安裝并使用Hexo
Hexo的安裝較為簡單,使用如下命令安裝
npm install -g hexo-cli #這里有一點要注意的就是,npm的源是在國外的,訪問可能會很慢,這里可以換成我們國內的源進行安裝加快速度。操作如下: npm config set registry https://registry.npm.taobao.org
3.1:初始化Hexo
上面的安裝完成之后執行下面的命令進行對Hexo進行一個初始化
#這個文件名字可以自己指定,之后會在當前目錄下生成對應文件夾 hexo init <文件名字> cd 文件名字 npm install
可以看到安裝好之后的一個目錄結構:
目錄文件說明:
_config.yml:網站的配置信息,您可以在此配置大部分的參數。
package.json:應用程序的信息。EJS, Stylus 和 Markdown renderer 已默認安裝,您可以自由移除。
scaffolds:模版文件夾。當您新建文章時Hexo 會根據 scaffold 來建立文件Hexo的模板是指在新建的文章文件中默認填充的內容。例如,如果您修改scaffold/post.md中的Front-matter內容,那么每次新建一篇文章時都會包含這個修改。
source:資源文件夾是存放用戶資源的地方。除 _posts
文件夾之外,開頭命名為 _
(下劃線)的文件 / 文件夾和隱藏的文件將會被忽略。Markdown 和 HTML 文件會被解析并放到 public
文件夾,而其他文件會被拷貝過去。
themes:主題 文件夾。Hexo 會根據主題來生成靜態頁面。
查看hexo的版本以及對應的數據:
3.2生成靜態文件,并開啟Hexo服務:
進入到了hexo的安裝目錄之后,使用hexo generate來生成靜態文件,也可以使用hexo g,之后使用hexo server(可以寫成hexo s)命令啟動服務,操作如下:
可以看到4000端口的服務已經開啟,之后在你的瀏覽器輸入http://<你的linux機器的ip地址或者服務器公網地址>:4000,如下可以看到最開始的一個界面:
4.初步使用Hexo:
使用前,我們對我們的站點進行一個配置,也就是我們創建的hexo目錄的_config.yml文件,可以修改的部分介紹如下:
# Site
title: QIMING.INFO #博客網站的標題
subtitle: #博客網站的副標題
description: #你的網站描述
keywords: #網站的關鍵詞
author: #作者的名字
language: #博客網站使用的語言
timezone: #網站時區
我自己的修改如下供大家參考,這里的修改沒有太大的限制:
4.1:開始使用Hexo發布自己的第一篇博客!
執行下面的目錄創建一篇新文章:
hexo new post <文章標題>
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9Tz5aBlT-1622032930755)(pictures/image-20210526145922392.png)]
這里我創建了一篇標題為First_Blog的博客,創建之后hexo目錄下面的source/_post文件夾下會產生一個First_Blog.md的文件
4.2:編輯文章
進入到上面說的那個目錄下可以看到我們創建的博客文件:
直接使用vim或者vi就可以對我們的博客文章進行編輯了,打開此First_Blog.md后可以看到—分隔的區域,這部分主要對文章進行標注變量,如下:
title:標題
tage:標簽
categories:分類
date:時間
這些標注大家在-----區域可以進行使用
4.3:發布文章
輸入如下命令,生成靜態網頁,靜態網頁會存放在public文件下
hexo g
hexo s
之后就可以去瀏覽器訪問了!可以看到我們發布的文章已經成功在瀏覽器顯示,到這里個人博客網站就已經成功搭建了。
5.主題的選擇:
主題網站:https://hexo.io/themes/ hexo提供了大量精美的主題供我們選擇,選擇喜歡的主題,在hexo目錄下的themes文件夾下使用git clone下載主題,之后再配置文件_config.yml把theme后面修改成下載的主題的名字,之后運行hexo clean ,hexo g即可看到生效的主題。
如果是有服務器的小伙伴,也可以將Hexo部署到服務器供全網訪問,服務器的購買這里就不多說,阿里云跟騰訊云上面對于學生也有較為優惠的價格。部署到服務器的話,就需要將上面的全部操作,在你的服務器系統上面執行,之后我們使用Nginx(反向代理服務器)進行部署。
Nginx安裝:
Nginx是一款高性能的 HTTP 和反向代理服務器,這里我們采用編譯安裝的方式,按照下面的指引依次執行命令
#安裝gcc編譯環境: yum install -y gcc-c++ #安裝zlib-devel庫: yum install -y zlib-devel #安裝OpenSSL密碼庫: yum install -y openssl openssl-devel #安裝pcre正則表達式庫:編譯nginx,需要需要指定pcre的路徑,這里我們選擇安裝穩定版本的。 下載地址:https://ftp.pcre.org/pub/pcre/ #選擇對應的版本下載下來之后上傳到我們的服務器,也可以使用wget直接下載 tar -xf pcre-8.43.tar.gz cd pcre-8.43 mkdir -p /usr/local/pcre
./configure --prefix=/usr/local/pcre make && make install
下載編譯安裝nginx:
nginx下載官網:http://nginx.org/en/download.html wget http://nginx.org/download/nginx-1.16.0.tar.gz mkdir -p /usr/local/nginx tar -xf nginx-1.16.0.tar.gz #編譯指定安裝路徑需要進入nginx cd nginx-1.16.0
./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-pcre #http_ssl_module 這是支持https的一個模塊,就是可以使用https://這樣去訪問。 make && make install #編譯安裝
啟動nginx服務:
#啟動: /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf #用指定配置文件的方式啟動 -c #測試: /usr/local/nginx/sbin/nginx -t #這個用于測試nginx的語法是否有問題 顯示is successful即為成功。 #關閉: /usr/local/nginx/sbin/nginx -s stop #繼續輸入以下命令使Nginx開機自動啟動: systemctl enable nginx #配置文件的位置:/usr/local/nginx/conf
之后我們需要配置服務器公網ip,編輯配置文件。
之后再重啟nginx服務,開啟hexo服務,這個時候使用公網的ip就可以訪問到我們的hexo服務了!
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
文章來源:csdn 作者:YO哥教你大數據
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
第一步:
-
//安裝
-
npm install crypto-js --save-dev
第二步:在src目錄下新建個放公用js文件夾(common),再建一個AES.js文件,例如:
第三步:在AES.js中填寫如下代碼,key密鑰長度則可以是128,192或256位(默認情況下是128位),正常情況下固定16位數即可
-
import CryptoJS from 'crypto-js';
-
-
export default {
-
//隨機生成指定數量的16進制key
-
generatekey(num) {
-
let library = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
let key = "";
-
for (var i = 0; i < num; i++) {
-
let randomPoz = Math.floor(Math.random() * library.length);
-
key += library.substring(randomPoz, randomPoz + 1);
-
}
-
return key;
-
},
-
-
//加密
-
encrypt(word, keyStr) {
-
keyStr = keyStr ? keyStr : 'abcdsxyzhkj12345'; //判斷是否存在ksy,不存在就用定義好的key
-
var key = CryptoJS.enc.Utf8.parse(keyStr);
-
var srcs = CryptoJS.enc.Utf8.parse(word);
-
var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 });
-
return encrypted.toString();
-
},
-
//解密
-
decrypt(word, keyStr) {
-
keyStr = keyStr ? keyStr : 'abcdsxyzhkj12345';
-
var key = CryptoJS.enc.Utf8.parse(keyStr);
-
var decrypt = CryptoJS.AES.decrypt(word, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 });
-
return CryptoJS.enc.Utf8.stringify(decrypt).toString();
-
}
-
-
}
第四步:在需要的地方引入
import AES from "@/common/AES.js";
第五步:調用
-
// var keys = AES.generatekey(16);
-
-
//如果是對象/數組的話,需要先JSON.stringify轉換成字符串
-
// 不傳key值,就默認使用上述定義好的key值
-
-
var encrypts = AES.encrypt(JSON.stringify(cars));
-
var dess = JSON.parse(AES.decrypt(encrypts));
-
-
// var encrypts = AES.encrypt('1234asdasd');
-
// var dess = AES.decrypt(encrypts);
-
-
console.log(encrypts)
-
console.log(encrypts.length)
-
console.log(dess)
特別提示:當解密的時候是為空的時候(也沒有報錯),那么就一定是你的key長度不符合規范, 可以調整為key長度為16位。
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
文章來源:csdn
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
首先項目是基于vue開發的項目
需要引入js
import cryptoJs from 'crypto-js'
// DES加密
export const encryptDes = (message, key) => {
return cryptoJs.DES.encrypt(message, cryptoJs.enc.Utf8.parse(key), {
mode: cryptoJs.mode.ECB,
padding: cryptoJs.pad.Pkcs7
}).toString()
}
package com.huihui.until;
import java.security.SecureRandom;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import org.apache.commons.codec.binary.Base64;
/**
* <b>類說明:DES</b>
* <p>
* <b>詳細描述:</b>
* @since 2019年3月31日 下午17:00:16
*/
public class DESCryptUtil {
private static final String DES = "DES";
public static final String desKey = "ba54ee44";
public static String doEncrypt(String plainMessage, String hexDesKey) throws Exception {
byte desKey[] = hexDesKey.getBytes();
byte desPlainMsg[] = plainMessage.getBytes();
return Base64.encodeBase64URLSafeString(desCrypt(desKey, desPlainMsg, Cipher.ENCRYPT_MODE));
}
/**
* 獲取解密后的字符串
* @param hexEncryptMessage
* @param hexDesKey
* @return
* @throws Exception
*/
public static String doDecrypt(String hexEncryptMessage, String hexDesKey) throws Exception{
if (hexEncryptMessage == null) {
return null;
}
byte desKey[] = hexDesKey.getBytes();
byte desPlainMsg[] = Base64.decodeBase64(hexEncryptMessage);
return new String(desCrypt(desKey, desPlainMsg, Cipher.DECRYPT_MODE));
}
/**
* 獲取解密后的數組
* @param desPlainMsg
* @param hexDesKey
* @return
* @throws Exception
*/
public static byte[] doDecryptByte(byte[] desPlainMsg, String hexDesKey) throws Exception{
if (desPlainMsg == null) {
return null;
}
byte desKey[] = hexDesKey.getBytes();
return desCrypt(desKey, desPlainMsg, Cipher.DECRYPT_MODE);
}
private static byte[] desCrypt(byte[] desKey, byte[] desPlainMsg, int CipherMode) throws Exception{
try {
SecureRandom sr = new SecureRandom();
DESKeySpec dks = new DESKeySpec(desKey);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
javax.crypto.SecretKey key = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance(DES);
cipher.init(CipherMode, key, sr);
return cipher.doFinal(desPlainMsg);
} catch (Exception e) {
String message = "";
if (CipherMode == Cipher.ENCRYPT_MODE) {
message = "DES\u52A0\u5BC6\u5931\u8D25";
} else {
message = "DES\u89E3\u5BC6\u5931\u8D25";
}
throw new Exception(message, e);
}
}
/**
* 獲取8位的key
* @param str
* @return
*/
public static String processString(String str) {
if(str==null||"".equals(str)) {
return "";
}
StringBuilder sb = new StringBuilder();
for(int i=0;i<8;i++) {
int index = i<<2&(32-i);
sb.append(str.charAt(index));
}
return sb.toString();
}
public static void main(String[] args) throws Exception{
DESCryptUtil se=new DESCryptUtil();
for (int i = 0; i < 5; i++) {
Scanner scanner=new Scanner(System.in);
/*
* 加密
*/
System.out.println("請輸入要加密的內容:");
String content = scanner.next();
System.out.println("加密后的密文是:"+se.doEncrypt(content, desKey));
/*
* 解密
*/
System.out.println("請輸入要解密的內容:");
content = scanner.next();
System.out.println("解密后的明文是:"+se.doDecrypt(content, desKey));
}
}
}
這是我是在在線生成公鑰私鑰的網站中生成了自己的公鑰私鑰用來測試
前臺
import JsEncrypt from 'jsencrypt'
// RSA加密
export function encryptRsa(publickey, message) {
const rsa = new JsEncrypt()
rsa.setPublicKey(publickey)
return rsa.encrypt(message)
}
后臺
package com.huihui.until;
import org.apache.commons.codec.binary.Base64;
import com.googosoft.config.GlobalConstants;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
public class RSAUtil {
private static Map<Integer, String> keyMap = new HashMap<Integer, String>(); //用于封裝隨機產生的公鑰與私鑰
public static void main(String[] args) throws Exception {
//生成公鑰和私鑰
genKeyPair();
//加密字符串
String message = "df723820";
//GlobalConstants.PUBLICKEY 公鑰加密
String messageEn = encrypt(message,GlobalConstants.PUBLICKEY);
System.out.println(message + "\t加密后的字符串為:" + messageEn);
//GlobalConstants.PRIVATEKEY 私鑰解密
String messageDe = decrypt(messageEn,GlobalConstants.PRIVATEKEY);
System.out.println("還原后的字符串為:" + messageDe);
}
/**
* 隨機生成密鑰對
* @throws NoSuchAlgorithmException
*/
public static void genKeyPair() throws NoSuchAlgorithmException {
// KeyPairGenerator類用于生成公鑰和私鑰對,基于RSA算法生成對象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密鑰對生成器,密鑰大小為96-1024位
keyPairGen.initialize(1024,new SecureRandom());
// 生成一個密鑰對,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 得到私鑰
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); // 得到公鑰
String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
// 得到私鑰字符串
String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
// 將公鑰和私鑰保存到Map
keyMap.put(0,publicKeyString); //0表示公鑰
keyMap.put(1,privateKeyString); //1表示私鑰
}
/**
* RSA公鑰加密
*
* @param str
* 加密字符串
* @param publicKey
* 公鑰
* @return 密文
* @throws Exception
* 加密過程中的異常信息
*/
public static String encrypt( String str, String publicKey ) throws Exception{
//base64編碼的公鑰
byte[] decoded = Base64.decodeBase64(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes("UTF-8")));
return outStr;
}
/**
* RSA私鑰解密
*
* @param str
* 加密字符串
* @param privateKey
* 私鑰
* @return 銘文
* @throws Exception
* 解密過程中的異常信息
*/
public static String decrypt(String str, String privateKey) throws Exception{
//64位解碼加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//base64編碼的私鑰
byte[] decoded = Base64.decodeBase64(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
}
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
文章來源:csdn
藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
一個前端小菜雞。若下邊的內容有瑕希望告訴我,如果有更好的方法希望告訴我,感謝萬分。
這篇文章主要介紹的對el-upload放在表單中提交之前rules的驗證。這里的圖片是必須提交項如果可以不提交可用常用方法直接提交就可以。
<el-form ref="personform" label-position="right" label-width="120px" :model="formLabelAlign" status-icon :rules="rules"> <el-row> <el-form-item label="簡述"> <el-input type="textarea" v-model="formLabelAlign.paper" autocomplete="off"></el-input> </el-form-item> </el-row> <el-row> <el-form-item prop="file" ref="uploadpic"> <el-upload
style="display:inline-block" :limit="2" class="upload-demo" ref="upload" action="/hqx/knowledge/importKnowledge" :before-upload="beforeAvatarUpload" :auto-upload="false" :on-change="imageChange" :on-remove="imageRemove" > <el-button slot="trigger" size="small" type="primary" plain>上傳</el-button> </el-upload> </el-form-item> </el-row> </el-form> <script> import _ from "lodash"; export default { data() { return { xiugai: false, formLabelAlign: { paper: "" }, images: [],// 圖片存儲 rules: { file: [{ required: true, message: "請上傳圖片", trigger: "change" }] }, haspic: false // 默認沒有傳圖片 }; }, methods: { beforeAvatarUpload(file) { // 文件類型進行判斷 const isJPG = /^image\/(jpeg|png|jpg)$/.test(file.type); const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this.$message.error("上傳圖片只能是 image/jpeg/png 格式!"); } if (!isLt2M) { this.$message.error("上傳圖片大小不能超過 2MB!"); } return isJPG && isLt2M; }, imageChange(file, fileList, name) {//on-change觸發 this.images["file"] = fileList; this.haspic = true; // 如果上傳了就不顯示提示圖片警告 if (typeof this.images.file != "undefined") { if (this.images.file.length > 0) { this.$refs["uploadpic"].clearValidate(); } } }, imageRemove(file, fileList, name) { //on-remove觸發 //如果images為空了說明并沒有提交圖片所以需要顯示警告 if (typeof this.images.file != "undefined") { if (this.images.file.length > 0) { this.$refs["uploadpic"].clearValidate(); } else { this.$refs["uploadpic"].validate(); this.haspic = false; } } }, confirm() {// 提交綁定的事件 if (this.haspic) { // 去掉rules中對圖片的驗證 _.unset(this.rules, ["file"]); this.$refs["personform"].validate(valid => { if (valid) { console.log("說明已經添加了圖片直接提交就行了"); const wfForm = new FormData(); wfForm.append( 'dsc',this.formLabelAlign.paper) Object.entries(this.images).forEach(file => { file[1].forEach(item => { wfForm.append('files', item.raw) wfForm.append(item.name, file[0]) }) }) // 直接提交 } else { console.log("error submit!!"); return false; } }); } else { // 向rules提價一條對圖片的驗證。 _.set(this.rules, "file", { required: true, message: "請上傳圖片", trigger: "change"}); this.$refs["personform"].validate(valid => { if (valid) { console.log("說明圖片沒有提交"); } else { console.log("error submit!!"); return false; } }); } } } }; </script>
下邊解釋一下每段代碼的含義:
1.
imageChange(file, fileList, name) {//on-change觸發 this.images["file"] = fileList; this.haspic = true; // 如果上傳了就不顯示提示圖片警告 if (typeof this.images.file != "undefined") { if (this.images.file.length > 0) { this.$refs["uploadpic"].clearValidate(); } } }
if (typeof this.images.file != “undefined”) 這個可加可不加
其中的一個原因是因為要頻繁對rules進行操作因為element的el-upload的提示功能在選擇了圖片的時候并不會對圖片的提示進行更改所以只能自己進行操作更改他顯示或者隱藏
haspic是用來記錄他是否上傳了圖片 如果上傳為true否則為false 在后面提交的時候有用。
2.考慮到用戶可能會選擇了圖片又刪除了所以加上了一個判斷
如果在提交的時候進行驗證或者不考慮用戶全部刪除顯示提示可不加
imageRemove(file, fileList, name) { //on-remove觸發 //如果images為空了說明并沒有提交圖片所以需要顯示警告 if (typeof this.images.file != "undefined") { if (this.images.file.length > 0) { this.$refs["uploadpic"].clearValidate(); } else { this.$refs["uploadpic"].validate(); this.haspic = false; } } },
confirm() {// 提交綁定的事件 if (this.haspic) { // 去掉rules中對圖片的驗證 _.unset(this.rules, ["file"]); this.$refs["personform"].validate(valid => { if (valid) { console.log("說明已經添加了圖片直接提交就行了"); const wfForm = new FormData(); wfForm.append( 'dsc',this.formLabelAlign.paper) Object.entries(this.images).forEach(file => { file[1].forEach(item => { wfForm.append('files', item.raw) wfForm.append(item.name, file[0]) }) }) // 直接提交 } else { console.log("error submit!!"); return false; } }); } else { // 向rules提價一條對圖片的驗證。 _.set(this.rules, "file", { required: true, message: "請上傳圖片", trigger: "change"}); this.$refs["personform"].validate(valid => { if (valid) { console.log("說明圖片沒有提交"); } else { console.log("error submit!!"); return false; } }); } } } };
提交的時候進行判斷。因為沒有想到其他的方法所以寫了一個變量判斷是否在rules加上對圖片的判斷。因為如果存在對圖片的判斷。form驗證的時候就總是throw error 不能進行提交操作this.$refs[“personform”].validate(valid){}是提交form表單的時的驗證
(1)在有圖片的時候去掉對圖片的驗證
(2)在有圖片的時候加上對圖片的驗證
<template> <div> <el-form ref="personform" label-position="right" label-width="120px" :model="formLabelAlign" status-icon :rules="rules"> <el-row> <el-col :span="12"> <el-form-item label="發布人"> <el-input size="mini" v-model="formLabelAlign.person" autocomplete="off" clearable :disabled="xiugai" ></el-input> </el-form-item> </el-col> </el-row> <el-row> <el-form-item label="簡述"> <el-input type="textarea" v-model="formLabelAlign.paper" autocomplete="off"></el-input> </el-form-item> </el-row> <el-row> <el-form-item prop="file" ref="uploadpic"> <el-upload
style="display:inline-block" :limit="2" class="upload-demo" ref="upload" action="/hqx/knowledge/importKnowledge" :before-upload="beforeAvatarUpload" :auto-upload="false" :on-change="imageChange" :on-remove="imageRemove" :on-success="onsuccess" > <el-button slot="trigger" size="small" type="primary" plain>上傳</el-button> </el-upload> </el-form-item> </el-row> </el-form> </div> </template> <script> import _ from "lodash"; export default { data() { return { xiugai: false, formLabelAlign: { paper: "" }, images: [], rules: { file: [{ required: true, message: "請上傳圖片", trigger: "change" }] }, haspic: false // 默認沒有傳圖片 }; }, methods: { beforeAvatarUpload(file) { // 文件類型進行判斷 const isJPG = /^image\/(jpeg|png|jpg)$/.test(file.type); const isLt2M = file.size / 1024 / 1024 < 2; if (!isJPG) { this.$message.error("上傳圖片只能是 image/jpeg/png 格式!"); } if (!isLt2M) { this.$message.error("上傳圖片大小不能超過 2MB!"); } return isJPG && isLt2M; }, imageChange(file, fileList, name) { this.images["file"] = fileList; this.haspic = true; // 如果上傳了就不顯示提示 if (typeof this.images.file != "undefined") { if (this.images.file.length > 0) { this.$refs["uploadpic"].clearValidate(); } } }, imageRemove(file, fileList, name) { if (typeof this.images.file != "undefined") { if (this.images.file.length > 0) { this.$refs["uploadpic"].clearValidate(); } else { this.$refs["uploadpic"].validate(); this.haspic = false; } } }, onsuccess(response, file, fileList){ // 如果提交失敗將haspic改為false后邊的數據就不讓他提交 }, confirm() { if (this.haspic) { // 去掉rules中對圖片的驗證 _.unset(this.rules, ["file"]); this.$refs["personform"].validate(valid => { if (valid) { console.log("說明已經添加了圖片直接提交就行了"); const wfForm = new FormData(); wfForm.append( 'dsc',this.formLabelAlign.paper) //直接將wfForm提交就可以 } else { console.log("error submit!!"); return false; } }); } else { alert('請添加圖片之后在提交') } } } }; </script>
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
文章來源:csdn
分享此文一切功德,皆悉回向給文章原作者及眾讀者.
免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
1.nodejs接收上傳的圖片主要是使用formidable模塊,服務器是使用的express搭建。
引入formidable
var formidable = require('./node_modules/formidable');
攔截請求,設置formidable的常規項
app.post("/image",function (req,res) { var form = new formidable.IncomingForm(); form.encoding = 'utf-8'; form.uploadDir = path.join(__dirname + "/../page/upload"); form.keepExtensions = true;//保留后綴 form.maxFieldsSize = 2 * 1024 * 1024; });
解析圖片,重命名圖片名稱,返回給前端
//處理圖片 form.parse(req, function (err, fields, files){ console.log(files.the_file); var filename = files.the_file.name var nameArray = filename.split('.'); var type = nameArray[nameArray.length - 1]; var name = ''; for (var i = 0; i < nameArray.length - 1; i++) { name = name + nameArray[i]; } var date = new Date(); var time = '_' + date.getFullYear() + "_" + date.getMonth() + "_" + date.getDay() + "_" + date.getHours() + "_" + date.getMinutes(); var avatarName = name + time + '.' + type; var newPath = form.uploadDir + "/" + avatarName; fs.renameSync(files.the_file.path, newPath); //重命名 res.send({data:"/upload/"+avatarName}) })
完整代碼如下
var path = require("path"); var fs = require("fs"); var express =require("./node_modules/express"); var app=express(); var bodyParser = require('./node_modules/body-parser'); var formidable = require('./node_modules/formidable'); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: true})); app.use(express.static(__dirname + "./../page")); app.listen("8083",function () { console.log("服務啟動") }); //攔截請求 app.post("/image",function (req,res) { var form = new formidable.IncomingForm(); form.encoding = 'utf-8'; form.uploadDir = path.join(__dirname + "/../page/upload"); form.keepExtensions = true;//保留后綴 form.maxFieldsSize = 2 * 1024 * 1024; //處理圖片 form.parse(req, function (err, fields, files){ console.log(files.the_file); var filename = files.the_file.name var nameArray = filename.split('.'); var type = nameArray[nameArray.length - 1]; var name = ''; for (var i = 0; i < nameArray.length - 1; i++) { name = name + nameArray[i]; } var date = new Date(); var time = '_' + date.getFullYear() + "_" + date.getMonth() + "_" + date.getDay() + "_" + date.getHours() + "_" + date.getMinutes(); var avatarName = name + time + '.' + type; var newPath = form.uploadDir + "/" + avatarName; fs.renameSync(files.the_file.path, newPath); //重命名 res.send({data:"/upload/"+avatarName}) }) });
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
文章來源:博客園
分享此文一切功德,皆悉回向給文章原作者及眾讀者.免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
PM2是node進程管理工具,可以利用它來簡化很多node應用管理的繁瑣任務,如性能監控、自動重啟、負載均衡等,而且使用非常簡單。
全局安裝
npm install -g pm2
挑express應用來舉例。一般我們都是通過npm start啟動應用,其實就是調用node ./bin/www。那么,換成pm2就是
注意,這里用了–watch參數,意味著當你的express應用代碼發生變化時,pm2會幫你重啟服務(長時間監測有可能會出現問題,這時需要重啟項目)
pm2 start ./bin/www –watch
參數說明:
–watch:監聽應用目錄的變化,一旦發生變化,自動重啟。如果要精確監聽、不見聽的目錄,最好通過配置文件。
-i –instances:啟用多少個實例,可用于負載均衡。如果-i 0或者-i max,則根據當前機器核數確定實例數目。
–ignore-watch:排除監聽的目錄/文件,可以是特定的文件名,也可以是正則。比如–ignore-watch=”test node_modules “some scripts”“
-n –name:應用的名稱。查看應用信息的時候可以用到。
-o –output :標準輸出日志文件的路徑。
-e –error :錯誤輸出日志文件的路徑。
–interpreter :the interpreter pm2 should use for executing app (bash, python…)。比如你用的coffee script來編寫應用。
完整命令行參數列表:地址
pm2 start app.js –watch -i 2
pm2 restart app.js
停止特定的應用??梢韵韧ㄟ^pm2 list獲取應用的名字(–name指定的)或者進程id。
pm2 stop app_name|app_id
如果要停止所有應用,可以
pm2 stop all
pm2 stop app_name|app_id
pm2 stop all
pm2 list
pm2 start app.js –watch
*這里是監控整個項目的文件
除了可以打開日志文件查看日志外,還可以通過pm2 logs來查看實時日志。
pm2 logs
pm2 save # 記得保存進程狀態
npm install pm2 -g
pm2 update
附pm2命令:
$ npm install pm2 -g # 命令行安裝 pm2 $ pm2 start app.js -i 4 # 后臺運行pm2,啟動4個app.js # 也可以把'max' 參數傳遞給 start # 正確的進程數目依賴于Cpu的核心數目 $ pm2 start app.js --name my-api # 命名進程 $ pm2 list # 顯示所有進程狀態 $ pm2 monit # 監視所有進程 $ pm2 logs # 顯示所有進程日志 $ pm2 stop all # 停止所有進程 $ pm2 restart all # 重啟所有進程 $ pm2 reload all # 0 秒停機重載進程 (用于 NETWORKED 進程) $ pm2 stop 0 # 停止指定的進程 $ pm2 restart 0 # 重啟指定的進程 $ pm2 startup # 產生 init 腳本 保持進程活著 $ pm2 web # 運行健壯的 computer API endpoint (http://localhost:9615) $ pm2 delete 0 # 殺死指定的進程 $ pm2 delete all # 殺死全部進程
藍藍設計建立了UI設計分享群,每天會分享國內外的一些優秀設計,如果有興趣的話,可以進入一起成長學習,請掃碼藍小助,報下信息,藍小助會請您入群。歡迎您加入噢~~希望得到建議咨詢、商務合作,也請與我們聯系。
文章來源:博客園
分享此文一切功德,皆悉回向給文章原作者及眾讀者.免責聲明:藍藍設計尊重原作者,文章的版權歸原作者。如涉及版權問題,請及時與我們取得聯系,我們立即更正或刪除。藍藍設計( www.syprn.cn )是一家專注而深入的界面設計公司,為期望卓越的國內外企業提供卓越的UI界面設計、BS界面設計 、 cs界面設計 、 ipad界面設計 、 包裝設計 、 圖標定制 、 用戶體驗 、交互設計、 網站建設 、平面設計服務
藍藍設計的小編 http://www.syprn.cn