Note source: Hook Education – big front end employment Training camp

Content: Notes, thoughts and experiences in the process of learning

Tip: project actual combat article resources can not be uploaded, only for reference

uni-app

Based on Vue+ wechat applet language system, cross-terminal development can be realized

Uni – app introduction

Uni-app is a framework that uses Vue to develop all front-end applications. Developers write a set of code, which can be published to IOS, Android, H5 and various small programs (wechat, Alipay, Baidu, Toutiao, Dingding) and other platforms, making it easy for developers to deliver quickly without changing their development thinking and habits

Why uniapp?

  • Developers, numerous cases
  • Platform capacity is not limited, through conditional compilation + platform specific API, does not affect other platforms
  • Excellent performance experience
  • Rich surrounding ecology
  • Low learning cost

Uni-app can call various SDKS and apis for its own use, thus generating multi-terminal code

Development environment Setup – HBuilderX

HBuilderX is a generic front-end development tool, but has been specially enhanced for UNI-App

It is recommended that you install the app development version out of the box, otherwise you will be prompted to install the plug-in when running and publishing

  • Download HBuilderX – Install
  • Create a new project, select UNI-app, enter the project name and create the project
  • Select run to run the program in multiple terminals, multiple terminals can run simultaneously

Initialize related configurations

Project directory structure

Note: Files in the static resource directory are not packed by Webpack, so files that need to be packed by Webpack cannot be placed here

  • Apply manifest.json configuration

    • File is the configuration file of the application, can be displayed in a visual way, used to specify the name of the application, icon, permissions, etc., we can also set the cross-domain interception handler for Vue for H5 in this
    • Here we need to fill in the little program appId
  • Compile configuration – vue.config.js

    • Not available in initialization state, an optional configuration file for configuring WebPack compilation options, official documentation
  • Global configuration – page.json -The official documentation

    • Global configuration of Uni-app, determining page file path, window style, native navigation bar, bottom native tabbar, etc., similar to app.json of applets

    • attribute type mandatory describe
      globalStyle Object no Sets the window appearance of the default page
      pages Object Array is Set the page path and window presentation
      easycom Object no Components automatically import rules
      tabBar Object no Sets the performance of the bottom TAB
      condition Object no Boot Mode Configuration
      subPackages Object Array no Subcontract loading configuration
      preloadRule Object no Subcontract pre-download rules
  • Global style – uni.scSS -The official documentation

    • Easy to control the overall application style, such as button color, border style, and a set of PRESET CSS variables
    • Is a special file that can be used in SCSS code without the need to import this file
  • Main component – app.vue

    • Note that it is not a page and cannot write teamplte syntax
    • The purpose of this file is to call application lifecycle functions, configure global styles, and configure global storage globalData
    • The application lifecycle can only be listened on app.vue, not on the page.
  • Entry file -main.js -The official documentation

    • Similar to vue, both are entry files for programs

Uni-app development specification and resource path

Development specification conventions

  • Page but file and VUE use the same, from top to bottom HTML, JS, CSS
  • Component tags are close to the applet specification and instead of using tags such as P and div, they use applet tags such as View and text
  • Interconnection capability (JS API) is close to wechat applets specification, but it needs to replace WX with UNI. See UNI-APP interface specification for details
  • Data binding and event handling are the same as the vue.js specification and complement the App and page life cycles
  • It is recommended that you develop with a Flex layout for multiterminal compatibility

Resource Path Description

Note: When writing uni-app code, it is recommended to use absolute paths (@/) instead of relative paths, but not out of bounds

  • To date static resources such as image, video, etc. SRC attributes within template, you can use relative or absolute paths as follows:

    • <! -- absolute path, /static: static directory in root directory, in cli project /static: static directory in SRC directory --> 
      <image class="logo" src="/static/logo.png"></image> 
      <image class="logo" src="@/static/logo.png"></image>
      
      <! -- Relative path --> 
      <image class="logo" src=".. /.. /static/logo.png"></image>
      Copy the code
    • The @ initial absolute and relative paths are verified by base64 conversion rules

    • Static resources for appointments are not converted to Base64 on non-H5 platforms

    • H5 platform, resources less than 4KB will be converted to Base64, the rest will not be transferred

  • Js file or script tag, you can use relative path and absolute path, the form is as follows:

    • // Absolute path, @ points to the project root directory, in cli projects @ points to the SRC directory
      import add from '@/common/add.js' 
      
      // Relative path
      import add from '.. /.. /common/add.js'
      Copy the code
  • The path of the image referenced in the CSS file or the style tag can be relative or absolute. The format is as follows:

    • /* Absolute path */ 
      background-image: url(/static/logo.png); 
      background-image: url(@/static/logo.png); 
      
      /* Relative path */ 
      background-image: url(. /.. /static/logo.png);
      Copy the code

The life cycle

Uni-app supports full Vue lifecycle hooks, as well as new application and page lifecyls

Application lifecycle: For the entire project, in app.vue

The function name instructions
onLaunch Triggered when uni-app initialization is complete (globally only triggered once)
onShow When uni-app is started, or from the background into the foreground display
onHide When UNI-app goes from the foreground to the background
onError Triggered when uni-app reports an error
onUniNViewMessage For details about how to listen data sent from nvUE to VUE, see NvUE Communication to VUE
onUnhandledRejection Reject an unprocessed Promise event listener function (2.8.1+)
OnPageNotFound (common) There are no listener functions on the page
onThemeChange Listen for system topic changes

Page life cycle: For pages

The function name instructions
onLoad Listen to the page load. The parameter is the data passed on the previous page and the parameter type is Object (used for page parameter transfer)
onShow Listen to the page display. Triggered each time a page appears on the screen, the package returns from a sub-page point to reveal the current page
onReady The listener page is rendered for the first time. Note that if the rendering is fast, it will trigger before the page is animated
onHide Listening page hiding
onUnload Listening page uninstallation
onResize Listen for window size changes
onPullDownRefresh Listen for user pull-down actions, usually used for pull-down refresh, and need to be allowed in pages. Json
onReachBottom An event to scroll to the bottom of a page (not to the bottom of a scroll view), usually used to pull down data from the next page. See notes below for details
onTabItemTap When TAB is clicked, the parameter is Object. For details, see notes below
onShareAppMessage Users click on the upper right to share
onPageScroll Listen for page scrolling with Object
onNavigationBarButtonTap Listen for the native title bar button click event with the Object parameter
onBackPress Listening page return
onNavigationBarSearchInputChanged Listen for changes in the native title bar search input box
onNavigationBarSearchInputConfirmed Listen for the native title bar search input box search event, triggered when the user clicks the “search” button on the soft keyboard.
onNavigationBarSearchInputClicked Listen for native title bar search input box click events
onShareTimeline Listen to the user click on the upper right corner to forward to moments
onAddToFavorites Listen to the user click favorites in the upper right corner

Route configuration and page redirection

The routing configuration

Uni-app page routing is all managed by the framework. Developers need to configure the path and page style of each routing page in pages. Json (similar to the configuration of page routing in app.json by applet).

"pages": [{"path": "pages/index/index"."style": {
      "navigationBarTitleText": "uni-app"
    }
  },{
    "path": "pages/new/new"."style": {
      "navigationBarTitleText": "new"}},]Copy the code

Routing hop

There are two ways to redirect a page:

  • Use the Navigator component to jump (tabbed navigation)
  • Call API jump (programmatic navigation)

Uni-app framework manages all current pages in the form of a stack. When route switching occurs, the page stack behaves as follows:

routing Page stack representation trigger
Initialize the New page is pushed The first page uni-app opens
Open a new page New page is pushed Call api-uni. navigateTo, use component
Page redirection The current page is out of the stack, and the new page is in the stack Call APluni.redirectToUsing components
The page returns The page goes off the stack until the target returns to the page Call APluninavigateBack, use components, users press the back button in the upper left corner, Android users click the physical Back button
The Tab to switch All pages are off the stack, leaving only new Tab pages Call APluni.switchTab, use component, user switch Tab
Heavy load All pages are off the stack, leaving only new pages 0 call APluni.reLaunchUsing components

NavigateTo: uni. NavigateTo: uni. SwitchTab: uni

Gets the current page stack

The getCurrentPages() function is used to get an instance of the current page stack, as an array in stack order, with the first element being the home page and the last element being the current page.

Note: getCurrentPages() is only used to display the page stack. Do not modify the page stack to avoid page status errors.

Routing and acceptance

Page pass parameters: used directly after jump routes? Parameter name = Parameter value & Parameter name = parameter valueTo pass

// Page jumps and passes parameters
uni.navigateTo({
  url: 'page2? name=liy&message=Hello' 
});
Copy the code

Page accepts parameters: The option parameter in the onLoad function that jumps to the target page retrieves the parameters passed in

// Page 2 receives parameters

// Option is of type object, which serializes the arguments passed on the previous page
onLoad: function (option) { 
  console.log(option.name); // Prints out the arguments passed from the previous page.
  console.log(option.message); // Prints out the arguments passed from the previous page.
}
Copy the code

Note: URL has length limit, too long string will fail to transfer, and non-standard character format may also lead to failure to transfer, so it is recommended to use encodeURI, decodeURI for complex parameters after processing transfer

Applets routing and subcontracting configuration

Due to the limitation of size and resource loading of applets, various applets platforms provide subcontracting methods to optimize the download and startup speed of applets.

The main package is where the default startup page and TabBar page are placed, and the subpackages are divided according to the configuration of pages.json.

When the small program starts, the main package will be downloaded and the page in the main package will be started by default. When the user enters a page in the subpackage, the corresponding subpackage will be automatically downloaded and displayed after downloading. At this time, there will be a waiting prompt in the terminal interface.

// The subcontract directory only needs to be manually written when it is created for the first time. Later, creating pages directly under the directory without manual writing will automatically be added to the subcontract JSON

// Subcontract configuration
"subPackages": [{
   // Subcontract catalog
  "root": "subPackages".// The page under the directory is written the same as the main package
  "pages": [{
    "path": "chat/chat"."style": {
      "navigationBarTitleText": "Talk to my soul mate."."enablePullDownRefresh": false}}}]].// Subcontract pre-download
"preloadRule": {
  // Page path
  "subPackages/chat/chat": {
    // Download conditions: all - under any network
    "network": "all"."packages": ["__APP__"]}},Copy the code

Common Components

Uni-app gives us a set of tag elements that are similar to HTML, but different from HTML, similar to applets, and more suitable for mobile use

While HTML tags are not recommended, they can be used and will automatically be converted when mutating to non-H5 platforms, but they are still not recommended

Commonly used API

H5 runs in the browser, non-H5 Android runs in the V8 engine, and iOS runs in the JScore engine of iOS. So the jsAPI of UNI-App consists of the STANDARD ECMAScript JS API and the UNI extension API.

ECMAScript is managed by Ecma International and is the basic JS syntax. Browser based on the standard JS extension window, Document AND other JS API; Node.js expands fs modules based on standard JS; Applets also extend various Wx.xx, my.xx, swan.xx apis based on standard JS.

There are many standard ECMAScript apis, such as console, setTimeout and so on.

Non-h5, although it does not support the JS API of window, Document, Navigator and other browsers, it does support standard ECMAScript.

Developers should not equate browser JS to standard JS.

So the non-H5 side of UNI-App also supports standard JS, if and for syntax, strings, arrays, time and other variables and various processing methods, but does not support browser-specific objects

Custom Components

Custom Components

Like Vue, which stores components in the component directory of the project, Uni-App only supports Vue single-file components (.vue components).

Components can be used using global registration and page import in three steps:

  • Import: import XXX from ‘XXX’

  • Use (‘xx’,xx) – global, Components :{XXX} – components

  • Use:

Intercomponent communication

Parent-child component communication

  1. The parent component passes data to the child component through custom properties

  2. The child component receives data passed by the parent component through props

  3. The parent component passes events to child components through custom event tags

  4. The child modifies the parent component data by triggering the parent component definition event

Slot Slot for data distribution and scope

  1. The parent component distributes slot to the child component by calling the nested HTML content inside the child component

  2. The child component communicates data to the parent component by adding properties to the slot tag, scoping the slot

Global event definition and communication

  1. Uni.$ON (event name, callback function (parameter)) can be used anywhere throughout the application to create a global event

  2. Uni. $emit (event name, parameter) can also be used anywhere in the entire application to trigger global events for multi-component data communication

Status management – vueX

  1. concept

Vuex is a state management mode developed specifically for vue.js applications. It uses centralized storage to manage the state of all components of an application and rules to ensure that the state changes in a predictable way.

  1. Application scenarios

Data or state needs to be shared between Vue components.

  1. The key rules
  • State: Stores status data
  • Getter: Derived data from State data, equivalent to the computed property of State
  • Mutation: Stores the method used to synchronize changed state data. The default parameter passed in is state
  • Action: Stores state data for asynchronous changes, not directly, but by triggering the Mutation method. The default parameter is context
  • Module: Vuex modularity
/ / create vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
	state: {userName: uni.getStorageSync('userName')? uni.getStorageSync('userName') : 'Not logged in user'
	},
	mutations: {MLOGIN(state, userName){
			uni.setStorageSync('userName', userName)
			state.userName = userName
		},
		MLOGOUT(state){
			uni.clearStorageSync()
			state.userName = 'Exit status user'}},actions: {login(context, userName){
			context.commit('MLOGIN', userName)
		},
		logout(context){
			context.commit('MLOGOUT')}}})export default store
Copy the code
/ / use vuex
import Vue from 'vue'
import App from './App'

Vue.config.productionTip = false

App.mpType = 'app'

// Call Store vuex state management
import store from '@/store/index.js'

if (process.env.NODE_ENV === 'development') {
	console.log('Development Environment')}else {
	console.log('Production environment')}const app = newVue({ ... App, store }) app.$mount()Copy the code
  1. use
// mapState - get all states, mapActions - get all Actions
import { mapState, mapActions } from 'vuex' 
export default { 
  // Calculate attributes: direct destruct
  computed: { ...mapState(['loginState'.'userInfo'])},// Method - direct deconstruction
  methods: { ...mapActions(['userLoginAction'.'userLogoutAction'])}}Copy the code

The operating environment is cross-compatible

Development environment and production environment

Uni-app uses process.env.node_env to determine whether the current environment is a development environment or a production environment. It is generally used to connect to test servers or production servers for dynamic switching.

In HBuilderX, clicking “run” to compile code is the development environment, and clicking “publish” to compile code is the production environment

if(process.env.NODE_ENV === 'development') {console.log('Development Environment')}else{
  console.log('Production environment'),}Copy the code

Judge platform

Platform judgment has two scenarios, one is at compile time and the other is at run time.

(Recommended) Compile-time check Compile-time check, that is, conditional compilation. Different platforms have different code after the package is compiled

// #ifdef H5 
alert("Only H5 platform has alert method") 
// #endif 

// The above code will only be compiled into the H5 distribution, other packages will not include the above code.
Copy the code

Run-time check Run-time check indicates that you still need to check the platform during runtime after the code has been sent to the package. In this case, use uni.getSystemInfosync (). Platform to check whether the client environment is Android, iOS or a small program development tool

switch(uni.getSystemInfoSync().platform){
  case 'android':
    console.log('Running on Android') 
    break; 
  case 'ios':
    console.log('Running on iOS') 
    break; 
  default:
    console.log('Running on developer tools') 
    break; 
}
Copy the code

Across the compatible

Uni-app has encapsulated common components and JS apis into the framework. Developers can develop according to uni-App specifications to ensure the compatibility of multiple platforms, and most services can be directly satisfied. However, each platform has its own characteristics, so some platforms cannot be cross-platform.

  • Writing a lot of if-else can lead to poor code performance and confusing management.
  • Compiling to a different project and then modifying it twice can make subsequent upgrades very troublesome.

C uses #ifdef, #ifndef to compile different code for Windows, MAC, and other oss. Uni-app takes this idea into consideration and provides uni-App with a conditional compilation method, which gracefully implements platform personalization in a single project.

Conditional compilation is marked with special comments that are used to compile code for different platforms at compile time.

Writing: Start with #ifdef or #ifndef plus %PLATFORM% and end with #endif.

  • \#ifdef: if defined exists only on a platform
  • \#ifndef: if not defined Exists except for a platform
  • %PLATFORM%: Platform name

%PLATFORM% can be:

value platform
APP-PLUS App
APP-PLUS-NVUE App nvue
H5 H5
MP-WEIXIN Wechat applets
MP-ALIPAY Alipay small program
MP-BAIDU Baidu applet
MP-TOUTIAO Bytedance applet
MP-QQ QQ small programs
MP-360 360 small programs
MP Wechat small program/Alipay small program/Baidu small program/bytedance small program /QQ small program /360 small program
QUICKAPP-WEBVIEW Quick Application General (including Alliance and Huawei)
QUICKAPP-WEBVIEW- UNION Fast Application Alliance
QUICKAPP-WEBVIEW- HUAWEI Quick Application Huawei

Bosom Brother project practice

  • Vuex data can be changed on the home page
  • Bosom brother page can realize dialogue function
{
	"pages": [{"path": "pages/index/index"."style": {
				"navigationBarTitleText": "uni-app"}}, {"path": "pages/me/me"."style": {
				"navigationBarTitleText": ""."enablePullDownRefresh": false}}, {"path": "pages/404/404"."style": {
				"navigationBarTitleText": ""."enablePullDownRefresh": false}}, {"path": "pages/list/list"."style": {
				"navigationBarTitleText": ""."enablePullDownRefresh": false}}, {"path": "pages/find/find"."style": {
				"navigationBarTitleText": ""."enablePullDownRefresh": false}}, {"path": "subpages/chat/chat"."style": {
				"navigationBarTitleText": ""."enablePullDownRefresh": false}}].// Subcontract page
	"subPackages": [{
		"root": "subPackages"."pages": [{
      // Open the page
			"path": "chat/chat"."style": {
				"navigationBarTitleText": "Talk to my soul mate."."enablePullDownRefresh": false}}, {"path": "new/new"."style": {
				"navigationBarTitleText": ""."enablePullDownRefresh": false}}}]]./ / preload
	"preloadRule": {
		"subPackages/chat/chat": {
			"network": "all"."packages": ["__APP__"]}},"globalStyle": {
		"navigationBarTextStyle": "black"."navigationBarTitleText": "uni-app"."navigationBarBackgroundColor": "#F8F8F8"."backgroundColor": "#F8F8F8"
	},
	"tabBar": {
		"color": "#7A7E83"."selectedColor": "#3cc51f"."borderStyle": "black"."backgroundColor": "#ffffff"."list": [{
			"pagePath": "pages/index/index"."iconPath": "static/tabbar-icons/wx.png"."selectedIconPath": "static/tabbar-icons/wx_s.png"."text": "WeChat"
		}, {
			"pagePath": "pages/list/list"."iconPath": "static/tabbar-icons/list.png"."selectedIconPath": "static/tabbar-icons/list_s.png"."text": "A list"
		}, {
			"pagePath": "pages/find/find"."iconPath": "static/tabbar-icons/find.png"."selectedIconPath": "static/tabbar-icons/find_s.png"."text": "Discovered"
		}, {
			"pagePath": "pages/me/me"."iconPath": "static/tabbar-icons/me.png"."selectedIconPath": "static/tabbar-icons/me_s.png"."text": "我"}}}]Copy the code
<template> <view class="content"> <! <navigator URL ="/subPackages/chat/chat" </navigator> <! Vuex --> <text> {{userName}}</text> <! -- Button to log in and log out, @click="login(' CWN ')"> </button> <button type="default" </button> </view> </template> <script> vuex import {mapState, MapActions} from 'vuex' export default {data() {return {}}, // MapActions (['login','logout'])}, // Calculates attributes to deconstruct data computed: {... mapState(['userName']) } } </script> <style> .content { display: flex; flex-direction: column; align-items: center; justify-content: center; } .logo { height: 200rpx; width: 200rpx; margin-top: 200rpx; margin-left: auto; margin-right: auto; margin-bottom: 50rpx; } .text-area { display: flex; justify-content: center; } .title { font-size: 36rpx; color: #8f8f94; } </style>Copy the code
/ / use vuex

import App from './App'
// Import the created vuex instance
import store from '@/store/index.js'

// #ifndef VUE3
import Vue from 'vue'
Vue.config.productionTip = false
App.mpType = 'app'
const app = newVue({ ... App,/ / use vuex
		store
})
app.$mount()
// #endif

// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
  const app = createSSRApp(App)
  return {
    app
  }
}
// #endif
Copy the code
// Introduce vue and vuex
import Vue from 'vue'
import Vuex from 'vuex'

// Globally register vuex
Vue.use(Vuex)

// Create an instance
const store = new Vuex.Store({
	/ / data
	state: {
		// Obtain from local data
		userName: uni.getStorageSync('userName')? uni.getStorageSync('userName') : 'Not logged in user'
	},
	// Change the method
	mutations: {
		/ / login
		TOIN(state, userName) {
			state.userName = userName
			uni.setStorageSync('userName', userName)
		},
		/ / exit
		TOOUT(state) {
			state.userName = 'Not logged in'
			uni.setStorageSync('userName')}},// Asynchronous method
	actions: {
		/ / login
		login(context, userName) {
			context.commit('TOIN',userName)
		},
		/ / exit
		logout(context) {
			context.commit('TOOUT')}}})export default store
Copy the code
<template> <view class="container"> <! <view class="chat-body"> <! <block v-for="(item,index) in chatList" :key='index'> <! <view class="chat-one" v-if="! item.isme"> <image class="chat-face" src="@/static/faces/1-thump.jpg" /> <view class="chat-box"> <view Class ="chat-sender"> <view > <view class="chat-content" V-if ="item.type === 'TXT '">{{item.content}}</view> <image class="chat-img" :src="item.content" mode="widthFix" v-else></image> </view> </view> <! -- My structure, <view class="chat-one chat-one-mine" V-else > <view class="chat-box"> <view class="chat-content" v-if="item.type  === 'txt'">{{item.content}}</view> <image class="chat-img" :src="item.content" mode="widthFix" v-else></image> </view> <image class="chat-face" src="@/static/faces/6-thump.jpg" /> </view> </block> </view> <! <view class="chat-footer"> <! <input class=" msG-input "type="text" cursor-spacing="16" V-model ="myInput"/> <! <image class="img-chose" SRC ="@/static/img. PNG "@click="choseImgAndSend" /> <! <view class="send-btn" @click="sendMsg"> send </view> </view> </template> <script> export default { Data () {return {myInput: ", // chatList: [{isme: false, type: 'TXT ', content: }, {isme: false, type: 'img', content: '/static/faces/6-thump.jpg'}, {isme: true, type: 'TXT ', content:' wow, you are beautiful !!!!'}, {isme: true, type: 'img', content: '/static/faces/6-thump.jpg' }, ] } }, If (uni.getStoragesync ('chatList')) {// Replace data this.chatList = Parse (uni.getStoragesync ('chatList')) // Slide to the bottom setTimeout(() => {uni.pagesCrollto ({scrollTop: 9999999, duration: Select uni. ChooseImage ({// only 1 count: SourceType: ['album', 'camera'], // success: This.chatlist.push ({isme: true, type: 'img', content: This.chatlist. Push ({isme: false, type: 'img', content: Res.tempfilepaths [0]}) // Turn bottom setTimeout(() => {uni.pagesCrollto ({scrollTop: 9999999, duration: SetStorageSync ('chatList', json.stringify (this.chatList)); }})}, // send text event function sendMsg() {// determine whether to enter if(this.myinput! This.chatlist.push ({isme: true, type: 'TXT ', content: this.myInput}) this.chatList.push({isme: true, type:' TXT ', content: this.myInput}) this.chatList.push({isme: False, type: 'TXT ', content: this.myInput}) setTimeout(() => {uni.pagescrollto ({scrollTop: 9999999, duration: 100})}, 5) // Save data uni.setStoragesync ('chatList', JSON.stringify(this.chatlist)); // reset input this.myInput ="}}}} </script> <style lang=" SCSS "scoped>. Container {background-color: #f1f1f1; min-height: 100vh; } .chat-body { padding-bottom: 178upx; .chat-time { text-align: center; color: #888; padding: 40upx 0 0; } .chat-one { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; align-items: flex-start; padding: 20upx 0; } .chat-one-begin { padding: 40upx 0 0; } .chat-one-mine { justify-content: flex-end; } .chat-face { width: 84upx; height: 84upx; border-radius: 10upx; margin-left: 40upx; } .chat-one-mine .chat-face { margin-left: 0; margin-right: 40upx; } .chat-box { max-width: calc(100% - 290upx); margin-left: 20upx; font-size: 30upx; } .chat-one-mine .chat-box { margin-right: 20upx; } .chat-sender { color: #888; font-size: 28upx; margin-top: -8upx; margin-bottom: 10upx; } .chat-content { background-color: #fff; border-radius: 5px; padding: 10px; .micon { margin-right: 20upx; color: #666; } } .chat-img { float: left; max-width: 60%; border-radius: 5px; } .chat-one-mine .chat-img { float: right; } } .chat-footer { width: 670upx; padding: 0 40upx; height: 120upx; position: fixed; bottom: 0; left: 0; background-color: #f1f1f1; display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-between; align-items: center; align-content: center; border-top: 1upx solid #ddd; .msg-input { background-color: #fff; width: calc(100% - 300upx); height: 70upx; line-height: 70upx; font-size: 30upx; border-radius: 10upx; padding: 0 20upx; } .img-chose { height: 70upx; width: 70upx; } .send-btn { height: 60upx; line-height: 60upx; width: 120upx; text-align: center; background-color: green; color: #FFFFFF; border-radius: 12upx; } } </style>Copy the code