制作一款好看的计算器

举报
空城机 发表于 2021/08/16 21:02:08 2021/08/16
【摘要】 微信小程序总是学习知识点也是很枯燥的,那么就做个小栗子来缓解一下学习的疲倦吧(更累了),做一款美观一些的微信小程序

微信小程序总是学习知识点也是很枯燥的,那么就做个小栗子来缓解一下学习的疲倦吧(更新累了)

像我学习小程序视频教程中也有说过做计算器,不过教程中的计算器太粗糙了,只能计算一下就结束了。 这可不行,男人要持久:dog:

所以我有感而发,准备做一个完整度高一些的简单计算器,计算器的基本功能就模仿win10自带的计算器吧,有加、减、乘、除、取余、正负这几块的功能,界面自己进行一下调整。

实现效果(图居然没录全,右侧少了一点):
在这里插入图片描述



正文开始

微信开发文档:https://developers.weixin.qq.com/miniprogram/dev/component/


创建页面

在微信小程序中,page页面是一个非常重要的概念,计算器页面可以在资源管理器中自己右键创建文件夹生成,也可以直接在app.json的page对象中输入页面地址和名称来生成。

app.json中例子如下:

"pages": [
    "pages/Calculator/Calculator",
    "pages/index/index",
    "pages/logs/logs",
],

ctrl + s保存后,资源管理器中就出现了Calculator的页面

在这里插入图片描述
页面一开始创建出来,在wxml中只有一行字,直接删掉即可。

关于navigationBarTitleText顶部标题栏的文字和背景颜色,可以在app.json中设置,也可以在页面的JS(Calculator.js)中进行设置

  • 在app.json中设置

在这里插入图片描述

  • Calculator.js中设置
onLoad() {
   wx.setNavigationBarTitle({
        title: '空城机の计算器',
    })
    wx.setNavigationBarColor({
        frontColor: '#ffffff',
        backgroundColor: '#C37D58',
    })
},

在这里插入图片描述



页面布局

在本次制作当中,为了在不同机型下自适应,我使用的尺寸格式是rpx,机型选择了iPhone6,这样 1px == 2rpx,将CSS编写的px像素大小乘2即可

页面总体分为三大块, 背景、屏幕、按键

背景我使用了image标签来做的,绝对定位,大小100%

在屏幕上,本来我想制作一条工具栏,暂时先放弃了

屏幕上目前分为计算栏和结果栏

按键界面上分为三块,一块是清除、数字正负、取余功能,一块是加减乘除等于这5个按键,另一块是数字按键部分

图示:
在这里插入图片描述



页面样式

页面的样式主要集中在wxss文件当中

屏幕和按键都是以毛玻璃效果为主,这样可以将底部的背景图模糊的展现出来

毛玻璃效果的制作以背景色background-color半透明为主,然后设置backdrop-filter blur滤镜模糊效果

backdrop-filter的MDN:https://developer.mozilla.org/zh-CN/docs/Web/CSS/backdrop-filter

这里的颜色大家可以自己自由选择搭配,比如我的屏幕CSS:

background-color: rgba(255, 255, 255, .3);  /* 背景色 */
box-shadow: 6rpx 6rpx 12px 6rpx rgba(0, 0, 0, .3);  /* 阴影 */
backdrop-filter: blur(20rpx);  /* 高斯滤镜 */

键盘的按键布局我使用了flex布局,这款布局非常好用,将上面的按键设置为宽25%,让flex-wrap: wrap;自动换行生效,按键就可以有效的排版布局了。

在设置0按键时,使用:nth-child子类选择即可
在这里插入图片描述

在点击键盘按键时,按键需要有一个动态的交互过程,在微信小程序中有一个hover-class的概念,在view上可以给hover-class附上一个class,这个class的样式就是按键按下时的样式,通常手指抬起后,这个样式的持续时间有400毫秒,这里可以使用hover-stay-time设置一个时间

顺便提一句,即使是在wxss中,依旧还是有:hover的存在,不过此时的:hover设置的就是按下去的样式,并且手指抬起不会恢复哦

在这里插入图片描述



数据渲染

在计算器下方的键盘上,按键有19个,如果一个按键一个按键写入wxml当中,就未免太过繁琐了,而且写点击方法时也比较麻烦

所以我将这些按钮的信息写入JS文件中,在Page()中存在data,这是页面的初始数据
设置keysdata数组对象,里面装了按键数组,每个按键都有name、index、type这三个属性。 type属性可以用来区分到底是计算符还是数字

/**
* 页面的初始数据
*/
data: {
   keysdata: [  // type:0 计算符  1 数字
       { name: 'AC', index: '001', type: 0 },
       { name: '+/-', index: '002', type: 0 },
       { name: '%', index: '003', type: 0 },
       { name: '÷', index: '004', type: 0 },
       { name: '7', index: '005', type: 1 },
       { name: '8', index: '006', type: 1 },
       { name: '9', index: '007', type: 1 },
       { name: '×', index: '008', type: 0 },
       { name: '4', index: '009', type: 1 },
       { name: '5', index: '0010', type: 1 },
       { name: '6', index: '0011', type: 1 },
       { name: '-', index: '0012', type: 0 },
       { name: '1', index: '0013', type: 1 },
       { name: '2', index: '0014', type: 1 },
       { name: '3', index: '0015', type: 1 },
       { name: '+', index: '0016', type: 0 },
       { name: '0', index: '0017', type: 1 },
       { name: '.', index: '0018', type: 1 },
       { name: '=', index: '0019', type: 0 },
   ],
   resultNum: '0',  //结果
   calNum1: '',  // 计算前位
   calNum2: '',  // 计算后位    
   calIcon: '',  // 计算符   
   calIconEQ: '',  // = 计算符   
   isCompute: false,  // 是否计算
   lastbtn: 0,  //上一个按钮  0:数字  1:计算符号  2:等于 3:清空 4:正负
},

在wxml的按键渲染中使用wx:for语句遍历渲染出按键

<view class="keyboards" wx:for="{{ keysdata }}" wx:key="index">
    <view class="keys" data-num="{{ item }}" hover-class="hoverkeys"
    hover-stay-time="100" bindtap="keyClick" >
            {{ item.name }}
    </view>
</view>


计算逻辑

这是整个过程中最复杂最困难的部分

一开始我并没有添加记录上一个按键的属性,这导致了我一开始的判断做的很累,而且还不完善

然后又把逻辑给重新写了一下

在按键按下去的功能中,最重要的是计算和等于两种
计算中按键判断大致相同,首先先准备一个计算方法compute,根据传入参数将加减乘除取余这五种操作写入进去
compute(num1, num2, way)参数有三个,前两个代表计算的数值,way表示方式
并且num1和num2因为判断的不同,可能传入的num1,num2数值会是空字符串。所以需要将数值根据方法不同来进行计算 (本来不想放代码的)

compute(num1, num2, way) {
    let res = 0;
    switch(way) {
        case '%':
            if (!num1 && num1 != 0) { num1 = '1' }
            if (!num2) { num2 = '1' }
            res = parseFloat(num1) % parseFloat(num2); break;
        case '÷':
            if (!num1) { num1 = '1' }
            if (!num2) { num2 = '1' }
            res = parseFloat(num1) / parseFloat(num2); break;
        case '×': 
            if (!num1) { num1 = '1' }
            if (!num2) { num2 = '1' }
            res = parseFloat(num1) * parseFloat(num2); break;
        case '-': 
            if (!num1) { num1 = '0' }
            if (!num2) { num2 = '0' }
            res = parseInt(num1) - parseInt(num2); break;
        case '+': 
            if (!num1) { num1 = '0' }
            if (!num2) { num2 = '0' }
            res = parseFloat(num1) + parseFloat(num2); break;
    }
    return res;
}

在计算过程栏和结果栏中需要用到上面数据渲染中data定义的calNum1、calNum2、calIcon等对象

 <!-- 计算过程 -->
 <view class="calcuProcess">
  {{ calNum1 }} {{ calIcon }} {{ calNum2 }} {{ calIconEQ }}
 </view>
 <!-- 结果和输入 -->
 <view class="inputNum">
   {{ resultNum }}
 </view>

在这里插入图片描述

calNum1 calIcon calNum2 calInconEQ resultNum
7 + 8 = 15

因为键盘按键是使用wx:for进行渲染的,data-num="{{ item }}"利用data-将按键数据绑定到元素上

所以可以使用bindtap="keyClick"来绑定keyClick点击方法
根据event的target来判断当前按下的是哪一个按键

let n = event.target.dataset.num;

先判断按键是否为数字按键或者是计算符按键

如果是数字按键,如果上一次按下的按键是计算符,那么结果栏的输入显示的resultNum数值先置为空字符串,之后再判断首字母是否是0,如果是08、012这类数字,将首字母的0去掉

如果按下的是计算符按键,那么判断就更加复杂了
1、每一种按键都有自身的逻辑方法,根据按键值进入不同的方法

// 判断执行哪个方法
switch (n.name) {
    case 'AC': this.clearScreen(); break;
    case '+/-': this.isNegative(); break;
    case '%': this.remainder(); break;
    case '÷': this.division(); break;
    case '×': this.multiplication(); break;
    case '-': this.reduce(); break;
    case '+': this.add(); break;
    case '=': this.equalRes(); break;
}        

2、当前按键值是加减乘除这类计算值
上一次的按键lastbtn为数字,那么将calNum1和结果栏上当前显示的数值进行计算
比如:let res = this.compute(data.calNum1, data.resultNum, '+')
上一次按键为等号 = ,将数据计算出来,然后去除calNum2和最后的等号
在这里插入图片描述
如果计算过程栏为空,直接按计算符,那么就是
在这里插入图片描述
最后不要忘记改变上一次的按键lastbtn记录和calIcon计算符
微信小程序和vue的区别在于这不是双向绑定的,虽然使用this.callcon = '某个值'可以改变当前的calIncon,但是页面wxml上并不会改变,所以需要使用setData方法来进行改变页面

3、当前按键值是等于号
判断在计算过程栏中是否有过等于符,如果有,那么继续那calNum1的值和结果栏值进行计算
如下示例:
在这里插入图片描述
如果接下来没有calNum2,判断上一次按键是否是数字或者等于符
在这里插入图片描述
如果上一次按键是计算符
在这里插入图片描述

4、清除符,将resultNum置为0,其他的calNum1等符号置为空即可
在这里插入图片描述


注意:这里像按键这些逻辑没有具体写,所以各位可以发散性的编写,并且我的逻辑并不一定是最优解,只能说给各位一个大致的方向



完整代码

Calculator.wxml

<!-- 背景图 -->
<image class="img" src="../../source/wei2.png"></image>
<!-- 屏幕部分 -->
<view class="calculateScreen">
  <!-- 功能栏 -->
  <view class="funBar"></view>
  <!-- 计算过程 -->
  <view class="calcuProcess">
   {{ calNum1 }} {{ calIcon }} {{ calNum2 }} {{ calIconEQ }}
  </view>
  <!-- 结果和输入 -->
  <view class="inputNum">
    {{ resultNum }}
  </view>  
</view>
<!-- 键盘界面 -->
<view class="keyboard">
    <view class="keyboards" wx:for="{{ keysdata }}" wx:key="index">
        <view class="keys" data-num="{{ item }}" hover-class="hoverkeys"
        hover-stay-time="100" bindtap="keyClick" >
                {{ item.name }}
        </view>
    </view>
</view>

Calculator.wxss

/* pages/Calculator/Calculator.wxss */
.img{
  position: absolute;
  top: 0rpx;
  width: 100%;
  height: 100vh;
  z-index: 0;
}
.calculateScreen {
  position: relative;
  width: 100%;
  height: 30vh;
  background-color: rgba(255, 255, 255, .3);
  z-index: 100;
  box-shadow: 6rpx 6rpx 12px 6rpx rgba(0, 0, 0, .3);
  overflow: hidden;
  /* 小程序的高斯模糊效果要加backdrop */
  backdrop-filter: blur(20rpx);
}
.funBar {
  width: 100%;
  height: 90rpx;
}
.calcuProcess {
  width: 100%;
  height: 90rpx;
  line-height: 90rpx;
  font-size: 32rpx;
  text-align: right;
  color: #1d1a1a;
}
.inputNum {
  width: calc(100vw - 40rpx);
  height: calc(100% - 200rpx);
  line-height: 115rpx;
  font-size: 80rpx;
  text-align: right;
  padding: 0rpx 20rpx;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-shadow: 5px 5px 5px #464443;
}

.keyboard {
  width: 100%;
  height: calc(70vh - 32rpx);
  padding-top: 12rpx;
  display: flex;
  flex-wrap: wrap;
  position: relative;
}

.keyboards {
  width: 25%;
  height: 20%;
  display: flex;
  justify-content:center;
  align-items: center;
}
.keys {
  width: 130rpx;
  height: 130rpx;
  border-radius: 50%;
  line-height: 130rpx;
  text-align: center;
  font-size: 60rpx;
  backdrop-filter: blur(10px);
  /* background-color: rgba(246,245,245, .6); */
  background: linear-gradient(145deg, rgba(235,228,228, .3), rgba(221,214,214, .2));
  box-shadow:  10px 10px 20px #8d8888,
             -10px -10px 20px #e2dfdf;
  color: #fff;
  text-shadow: 5px 5px 5px #992002;
}
.hoverkeys {
  background: linear-gradient(145deg, rgba(221,214,214, .6), rgba(235,228,228, .7));
  box-shadow:  10px 10px 30px #302a2a,
             -10px -10px 30px #726060;
}

.keyboards:nth-child(17) {
  width: 50%;
}
.keyboards:nth-child(17) .keys {
  width: 300rpx;
  border-radius: 130rpx;
}

Calculator.js

// pages/Calculator/Calculator.js
Page({
    onLoad() {
        wx.setNavigationBarTitle({
            title: '空城机の计算器',
        })
        wx.setNavigationBarColor({
            frontColor: '#ffffff',
            backgroundColor: '#C37D58',
        })
    },
    /**
     * 页面的初始数据
     */
    data: {
        keysdata: [ // type:0 计算符  1 数字
            { name: 'AC', index: '001', type: 0 },
            { name: '+/-', index: '002', type: 0 },
            { name: '%', index: '003', type: 0 },
            { name: '÷', index: '004', type: 0 },
            { name: '7', index: '005', type: 1 },
            { name: '8', index: '006', type: 1 },
            { name: '9', index: '007', type: 1 },
            { name: '×', index: '008', type: 0 },
            { name: '4', index: '009', type: 1 },
            { name: '5', index: '0010', type: 1 },
            { name: '6', index: '0011', type: 1 },
            { name: '-', index: '0012', type: 0 },
            { name: '1', index: '0013', type: 1 },
            { name: '2', index: '0014', type: 1 },
            { name: '3', index: '0015', type: 1 },
            { name: '+', index: '0016', type: 0 },
            { name: '0', index: '0017', type: 1 },
            { name: '.', index: '0018', type: 1 },
            { name: '=', index: '0019', type: 0 },
        ],
        resultNum: '0',  //结果
        calNum1: '',  // 计算前位
        calNum2: '',  // 计算后位    
        calIcon: '',  // 计算符   
        calIconEQ: '',  // = 计算符   
        isCompute: false,  // 是否计算
        lastbtn: 0,  //上一个按钮  0:数字  1:计算符号  2:等于 3:清空 4:正负
    },

    // 键盘点击事件
    keyClick(event) {
        
        let n = event.target.dataset.num;
        
        if (n.type == 1) {
            if (this.data.lastbtn == 1) {
                this.data.resultNum = ''
            }
            if (this.data.resultNum == '0') {
                if(n.name != '.')
                    this.data.resultNum = n.name;
                else 
                this.data.resultNum += n.name;
            } else {
                if( this.data.lastbtn == '2' ) {
                    // 是否等于过
                    if (this.data.calIconEQ) {
                        this.clearScreen();
                    }
                    this.data.resultNum = '';
                }
                this.data.resultNum += n.name;
            }
            
            this.setData({ resultNum: this.data.resultNum })
            this.data.lastbtn = 0;  //更新最新按下按钮
        } else if(n.type == 0){    
            // 判断执行哪个方法
            switch (n.name) {
                case 'AC': this.clearScreen(); break;
                case '+/-': this.isNegative(); break;
                case '%': this.remainder(); break;
                case '÷': this.division(); break;
                case '×': this.multiplication(); break;
                case '-': this.reduce(); break;
                case '+': this.add(); break;
                case '=': this.equalRes(); break;
            }         
        }        
    },
    // 相加
    add () {
        let data = this.data;
        if (data.lastbtn == 1 && data.calIcon == '+') return ;        
        // 上一次数字
        if (data.lastbtn == 0) {
            let res = this.compute(data.calNum1, data.resultNum, '+')
            this.setData({ calNum1: res }) 
            this.setData({ resultNum: res }) 
        }
        // 上一次等于
        if (data.lastbtn == 2 && data.calIconEQ) {
            this.setData({ calNum1: data.resultNum }) 
            this.setData({ calNum2: '' })
            this.setData({ calIconEQ: '' })
        }
        // 如果界面为空
        if (!data.calNum1 && !data.calIcon) {
            this.setData({ calNum1: data.resultNum });
        }
        this.setData({ calIcon: '+' });
        data.lastbtn = 1;  //更新最新按下按钮
    },
    // 清除
    clearScreen () {
        this.setData({ resultNum: '0' });
        this.setData({ calNum1: '' });
        this.setData({ calNum2: '' });
        this.setData({ calIcon: '' });
        this.setData({ calIconEQ: '' });
        this.data.lastbtn = 3;  //更新最新按下按钮
        console.clear()
    },
    // 正负
    isNegative (){
        let data = this.data;
        data.resultNum = parseFloat(data.resultNum);  //数值变字符串
        if (data.resultNum > 0){
            this.setData({ resultNum: -data.resultNum }) 
        } else {
            this.setData({ resultNum: Math.abs(data.resultNum) }) 
        }
        data.lastbtn = 4;  //更新最新按下按钮
    },
    // 余数
    remainder () {
        let data = this.data;
        if (data.lastbtn == 1 && data.calIcon == '%') return ;
        // 上一次数字
        if (data.lastbtn == 0) {
            if (!data.calNum1 && !data.calIcon) {
                this.setData({ calNum1: data.resultNum }) 
            } else {
                let res = this.compute(data.calNum1, data.resultNum, '%')
                this.setData({ calNum1: res }) 
                this.setData({ resultNum: res }) 
            }
        }
        // 上一次等于
        if (data.lastbtn == 2 && data.calIconEQ) {
            this.setData({ calNum1: data.resultNum }) 
            this.setData({ calNum2: '' })
            this.setData({ calIconEQ: '' })
        }
        // 如果界面为空
        if (!data.calNum1 && !data.calIcon) {
            this.setData({ calNum1: data.resultNum });
        }
        this.setData({ calIcon: '%' });
        data.lastbtn = 1;  //更新最新按下按钮
    },
    // 除法
    division () {
        let data = this.data;
        if (data.lastbtn == 1 && data.calIcon == '÷') return ;
        
        if (data.calIconEQ) {
            
        }
        // 上一次数字
        if (data.lastbtn == 0) {
            if (!data.calNum1 && !data.calIcon) {
                this.setData({ calNum1: data.resultNum }) 
            } else {
                let res = this.compute(data.calNum1, data.resultNum, '÷')
                this.setData({ calNum1: res }) 
                this.setData({ resultNum: res }) 
            }
        }
        // 上一次等于
        if (data.lastbtn == 2 && data.calIconEQ) {
            this.setData({ calNum1: data.resultNum }) 
            this.setData({ calNum2: '' })
            this.setData({ calIconEQ: '' })
        }
        // 如果界面为空
        if (!data.calNum1 && !data.calIcon) {
            this.setData({ calNum1: data.resultNum });
        }
        this.setData({ calIcon: '÷' });
        data.lastbtn = 1;  //更新最新按下按钮
    },
    // 乘法
    multiplication () {
        let data = this.data;
        if (data.lastbtn == 1 && data.calIcon == '×') return ;
        // 上一次数字
        if (data.lastbtn == 0) {
            let res = this.compute(data.calNum1, data.resultNum, '×')
            this.setData({ calNum1: res }) 
            this.setData({ resultNum: res }) 
        }
        // 上一次等于
        if (data.lastbtn == 2 && data.calIconEQ) {
            this.setData({ calNum1: data.resultNum }) 
            this.setData({ calNum2: '' })
            this.setData({ calIconEQ: '' })
        }
        // 如果界面为空
        if (!data.calNum1 && !data.calIcon) {
            this.setData({ calNum1: data.resultNum });
        }
        this.setData({ calIcon: '×' });
        data.lastbtn = 1;  //更新最新按下按钮
    },
    // 减法
    reduce() {
        let data = this.data;
        if (data.lastbtn == 1 && data.calIcon == '-') return ;
        // 上一次数字
        if (data.lastbtn == 0) {
            if (!data.calNum1 && !data.calIcon) {
                this.setData({ calNum1: data.resultNum }) 
            } else {
                let res = this.compute(data.calNum1, data.resultNum, '-')
                this.setData({ calNum1: res }) 
                this.setData({ resultNum: res }) 
            }
        }
        // 上一次等于
        if (data.lastbtn == 2 && data.calIconEQ) {
            this.setData({ calNum1: data.resultNum }) 
            this.setData({ calNum2: '' })
            this.setData({ calIconEQ: '' })
        }
        // 如果界面为空
        if (!data.calNum1 && !data.calIcon) {
            this.setData({ calNum1: data.resultNum });
        }
       
        this.setData({ calIcon: '-' });
        data.lastbtn = 1;  //更新最新按下按钮
    },
    // 结果
    equalRes() {
        let data = this.data;
        let res;
        // 判断是否等于过
        if (data.calIconEQ) {
            res = this.compute(data.calNum1, data.calNum2, data.calIcon);
            this.setData({ calNum1: res }) 
            this.setData({ resultNum: this.compute(res, data.calNum2, data.calIcon) }) 
        } else {
            if (data.lastbtn == 2) return ;
            // 判断有没有后一个数
            if (this.data.calNum2) {} else {
                if (data.lastbtn == 3) {
                    this.setData({ calNum1: data.resultNum }) 
                    this.setData({ calIcon: '=' });
                }
                if (data.lastbtn == 0 || data.lastbtn == 4) {
                    if (data.calIcon && data.calIcon != '=') {
                        res = this.compute(data.calNum1, data.resultNum, data.calIcon);
                        this.setData({ calNum2: data.resultNum });
                        this.setData({ calIconEQ: '=' });
                        this.setData({ resultNum: res });
                    } else {
                        this.setData({ calNum1: data.resultNum }) 
                        this.setData({ calIcon: '=' });
                    }
                }
                if (data.lastbtn == 1) {
                    // console.log(data.resultNum)
                    this.setData({ calNum1: data.resultNum })
                    res = this.compute(data.calNum1, data.resultNum, data.calIcon);
                    this.setData({ calNum2: data.resultNum }) 
                    this.setData({ resultNum: res }) 
                    this.setData({ calIconEQ: '=' });
                }
                
            }
        }       
        data.lastbtn = 2;  //更新最新按下按钮
    },
    compute(num1, num2, way) {
        let res = 0;
        switch(way) {
            case '%':
                if (!num1 && num1 != 0) { num1 = '1' }
                if (!num2) { num2 = '1' }
                res = parseFloat(num1) % parseFloat(num2); break;
            case '÷':
                if (!num1) { num1 = '1' }
                if (!num2) { num2 = '1' }
                res = parseFloat(num1) / parseFloat(num2); break;
            case '×': 
                if (!num1) { num1 = '1' }
                if (!num2) { num2 = '1' }
                res = parseFloat(num1) * parseFloat(num2); break;
            case '-': 
                if (!num1) { num1 = '0' }
                if (!num2) { num2 = '0' }
                res = parseInt(num1) - parseInt(num2); break;
            case '+': 
                if (!num1) { num1 = '0' }
                if (!num2) { num2 = '0' }
                res = parseFloat(num1) + parseFloat(num2); break;
        }
        return res;
    }
})
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。