技术研究之UniApp

标签: 无 分类: 未分类 创建时间:2023-10-03 04:29:48 更新时间:2024-11-23 10:24:25

1.前言

最近的一个项目就是使用 uni-app 进行开发,一开始的时候,就是找不清楚到底如何才能引入uni-app 的组件,

2.底部导航按钮

底部的tabbar导航栏,我刚开始的使用就是使用这个 custom-tab-bar 组件,但是我直接写的话,好像还是有问题的:TypeError: instance.update is not a function。

1
2
3
4
5
<template>
<view>
<custom-tab-bar direction="horizontal" :show-icon="false" :selected="selected" @onTabItemTap="onTabItemTap" />
</view>
</template>

问题总是很多的,不知道该如何才能配置成功。

后来我换了一个方式,就是使用tabbar的方式进行配置。

(1)创建三个文件。
(2)编写page.json配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
}
,{
"path" : "pages/renfang/index",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}

}
,{
"path" : "pages/map/index",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}

}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/image/icon_component.png",
"selectedIconPath": "static/image/icon_component_HL.png",
"text": "全局"
}, {
"pagePath": "pages/renfang/index",
"iconPath": "static/image/icon_API.png",
"selectedIconPath": "static/image/icon_API_HL.png",
"text": "设施"
},{
"pagePath": "pages/map/index",
"iconPath": "static/image/icon_API.png",
"selectedIconPath": "static/image/icon_API_HL.png",
"text": ""
}]
}
}

(3)自动生成了导航栏。

参考文章:
1.custom-tab-bar 我仔细的阅读了这个文章:在小程序和App端,为提升性能,在 pages.json 里配置固定的原生tabBar。但在H5端,这一设计并不会提升性能。同时,H5端尤其是PC宽屏,对tabBar的位置和样式有更灵活的需求,tabBar作为一级导航,更多的时候是在PC网页顶部而不是底部。自定义tabBar组件应需而生,它仍然读取 pages.json 里配置的tabBar信息,但这个组件可以自定义摆放的位置,可以灵活的配置各种css。该组件支持 pages.json 中 tabBar 相关配置(兼容性和 H5 保持一致), 其中不支持 borderStyle 配置。
该组件支持所有 tabBar 相关 API,例如设置 tab 徽标、显示红点、以及 switchTab 等。
2.tabbar添加list报错 已找到原因,需要添加2个以上才不会报错….
3.tabBar 这里是一个讨论,算是bug报告吧。tabBar 中的 list 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序。
4.2020-11-16 uni-app tabbar底部导航配置 这篇文章没有自定义,只是使用了uniapp自带的,另外加了字体。

3.引入字体

引入字体的方式和在web端引入的方式一样。

(1) 在组件仓库中进行安装

(2) 重新编译运行

(3) 引入

1
<uni-icons type="contact" size="30"></uni-icons>

4.自定义导航按钮

我想实现的功能就是类似于京东、天猫一样的在顶部导航栏下面的一排的导航按钮的形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<template>
<view class="main">
<view class="navigator">
<view class="navigator-item" v-for="(item,index) in dataState.tabBar.list" :key="item.pagePath"
@click="switchTab(index,item)">
<img :src="item.iconPath" class="icon" v-if="dataState.selectedIndex !== index">
<img :src="item.selectedIconPath" class="icon" v-else>
<text :class="['item-text',{'text-active':dataState.selectedIndex === index}]">{{item.text}}</text>
</view>
</view>
<view class="content">

</view>
</view>

</template>

<script setup>
import { reactive } from "vue";
const dataState=reactive({
tabBar:{
color:"#000",
selectedColor:"#ff161a",
list:[
{
"pagePath":"static/image/icon-gcgl",
"iconPath":"static/image/icon-gcgl.png",
"selectedIconPath":"static/image/icon-gcgl.png",
"text":"空间管理"
},
{
"pagePath":"static/image/icon-gcmj",
"iconPath":"static/image/icon-gcmj.png",
"selectedIconPath":"static/image/icon-gcmj.png",
"text":"工程面积\n年增长"
},
{
"pagePath":"static/image/icon-",
"iconPath":"static/image/icon-gcgn.png",
"selectedIconPath":"static/image/icon-gcgn.png",
"text":"工程功能\n各类数量"
},
{
"pagePath":"static/image/icon-",
"iconPath":"static/image/icon-ssdt.png",
"selectedIconPath":"static/image/icon-ssdt.png",
"text":"实时动态"
}
]
}, // 菜单列表
selectedIndex:0, // 选中的序号
})

/**
* 切换页面
*/
const switchTab=(index,item)=>{
console.log(index,item);
dataState.selectedIndex=index;
}
</script>

<style lang="less" scoped>
.main{
width: 100%;
height: 100%;
position: absolute;
display: flex;
flex-direction: column;

.navigator{
display: flex;
border-bottom: 5px solid #eee;

.navigator-item{
flex: 1;
display: flex;
flex-direction: column;
text-align: center;
padding:15px 5px;

img{
width: 80rpx;
left: 50%;
position: relative;
transform: translateX(-50%);
margin-bottom: 10px;
}

.text-active{
color: rgba(62, 132, 243, 1);
}
}
}
.content{
background:url('../../static/image/page-bg.png');
flex: 1;
overflow-x: hidden;
}
}

</style>
参考文章:
1.uniapp自定义tabBar 这里是用了一个自定义的方式实现了tabbar的嵌入,还可以增加顶部的tabbar方式。
2.uniapp 顶部tabBar

5.配置环境变量

主要有三种方式:

  • vue.config.js 或者是 vite.config.js 配置文件,在 Cli工程 和 HBuilderX创建的项目 中都可以使用。
  • .env 文件,只有在Cli工程中可以使用。
  • package.json 文件,在 Cli工程 和 HBuilderX创建的项目 中都可以使用。

这里我才用的是自定义的方式
(1) 项目根目录下新建 .env.development.js 文件

1
2
3
4
export default {
env: 'development',
request_baseurl: 'http://development',
};

(2) 项目根目录下新建 .env.production.js 文件

1
2
3
4
export default {
env: 'production',
request_baseurl: 'http://production',
};

(3) 项目根目录下新建 .env 文件

1
2
3
4
5
6
7
import development from './.env.development';
import production from './.env.production';

export default {
development,
production
}

(4) 修改vite.config.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { defineConfig } from 'vite';
import uni from '@dcloudio/vite-plugin-uni';
import env from './.env';

/**
* 获取配置内容
*/
function getConfig(){
let envConfig=env[process.env.NODE_ENV];
let temp={};
for(let name in envConfig){
temp['process.env.'+name]=JSON.stringify(envConfig[name]);
}
return temp;
}

export default defineConfig({
plugins: [uni()],
define: getConfig(),
});

(5) 直接在项目中使用

1
console.log(process.env.request_baseurl);
参考文章:
1.uni-app 项目中配置环境变量主要有如下三种方式
2.HBuilderx uni-app配置环境变量 / 自定义环境变量 主要是使用了 package.json 文件的方式进行配置
3.vue.config.js 配置 vue.config.js
4.vite.config.js 配置 vite.config.js
5.uni-app之使用Vite.config.js配置文件的详细教程
6.uni-app 环境变量 配置
7.uni-app的多环境部署配置 使用基于vue-cli命令行方式创建项目。
8.uniapp(vue3+vite)环境变量配置 使用 package.json 文件创建环境变量。
9.uniapp-vue3-vite模板搭建

问题

(1)vite.config.js不生效
如果编写了 vite.config.js 不生效,需要点击控制台,然后选择重新编译项目,才能有用。

(2)define定义的是一个变量
在正常的思维中,下面的 process.env.VUE_APP_CONFIG 配置为d,这个d会被认为是一个字符串,但是实际上不是的,它会被识别为一个变量。

1
2
3
define: {
'process.env.VUE_APP_CONFIG': "d",
},

【解决方案】
解决方法就是 JSON.stringify 进行序列化。这个在uni-app的说明中,也是这么用的。

1
2
3
define: {
'process.env.VUE_APP_CONFIG': JSON.stringify("d"),
},

6.uni.request封装

(1)封装request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* 封装Request方法
*/
export default function http(param) {
// 请求参数
let url = param.url,
method = param.method,
header = param.header||{},
data = param.data || {},
token = param.token || "",
hideLoading = param.hideLoading || false;

let baseUrl=process.env.VITE_GLOB_DOMAIN_URL;
//拼接完整请求地址
var requestUrl = baseUrl + url;

//请求方式:GET或POST(POST需配置
// header: {'content-type' : "application/x-www-form-urlencoded"},)
if (method) {
method = method.toUpperCase(); //小写改为大写
}
// 默认采用的是 application/json 方式请求,表单请求:"application/x-www-form-urlencoded"
if(!header['content-type']){
header['content-type'] = "application/json";
}

//加载圈
if (!hideLoading) {
uni.showLoading({
title: '加载中...'
});
}

// 返回promise
return new Promise((resolve, reject) => {
// 请求
uni.request({
url: requestUrl,
data: data,
method: method,
header: header,
success: (res) => {
// 判断 请求api 格式是否正确
if (res.statusCode && res.statusCode != 200) {
uni.showToast({
title: "api错误" + res.errMsg,
icon: 'none'
});
return;
}
// 将结果抛出
resolve(res.data)
},
//请求失败
fail: (e) => {
uni.showToast({
title: "" + e.data.msg,
icon: 'none'
});
resolve(e.data);
},
//请求完成
complete() {
//隐藏加载
if (!hideLoading) {
uni.hideLoading();
}
resolve();
return;
}
})
})
}

(2)创建api接口
创建一个独立的 api 文件,定义需要请求的url。

1
2
3
4
5
6
import http from '../utils/request.js'
export const getCodeInfo = (params) => {
return http({
url:"/sys/randomImage"+`/${params}`,
})
}

(3)引用api接口

1
2
3
4
5
6
7
import { getCodeInfo } from '../../api/index.js';

randCodeData.checkKey = 1629428467008;
getCodeInfo(randCodeData.checkKey).then((res) => {
randCodeData.randCodeImage = res;
randCodeData.requestCodeSuccess = true;
});

7.路径别名

在使用 HBuilderX 创建的项目中,不需要单独的配置 vite.config.js 文件,默认的 @ 就是项目的根目录。

如果使用的是 Cli 进行创建的,@ 符合表示的就是 src 目录。

8.Pinas

(1) main.js中增加如下代码

1
2
3
4
5
6
7
8
9
10
11
import { createSSRApp } from 'vue';
import * as Pinia from 'pinia';

export function createApp() {
const app = createSSRApp(App);
app.use(Pinia.createPinia());
return {
app,
Pinia, // 此处必须将 Pinia 返回
};
}

(2) 创建store

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// stores/counter.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
state: () => {
return { count: 0 };
},
// 也可以这样定义
// state: () => ({ count: 0 })
actions: {
increment() {
this.count++;
},
},
});

(3) 在组件中使用

1
2
3
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
counter.count++
参考文章:
1.状态管理 Pinia uni-app 内置了 Pinia, 使用 HBuilder X 不需要手动安装,直接使用即可。

9.二级目录

发行的h5网页端,要想配置成二级目录,需要单独的进行设置。

(1)打开项目根目录下的 “manifest.json 文件”,找到 “web 配置”,然后 找到运行基础路径,编写需要发布的路径。

(2)配置nginx,进行二级目录的编写。

这样配置带来的副作用,就是page.json中写的那个 tabbar 的路径就有问题了。

1
2
3
4
5
6
7
8
9
10
11
12
"tabBar": {
"color": "rgba(166, 166, 166, 1)",
"selectedColor": "rgba(62, 132, 243, 1)",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/image/icon-index.png",
"selectedIconPath": "static/image/icon-index-select.png",
"text": "index"
}]
}

解决方法就是,对基础路径增加一个 斜杠,由 “h5”,变为 “/h5/” 。

参考文章:
1.关于uni-app发布H5在域名二级目录下的问题 这个直接就是编写的json文件。
2.uni-app 部署到二级目录的问题 这里有一个图片进行了配置说明。

10.图表

10.1.安装图表

安装图表的方法还是挺麻烦的,要用微信扫码看广告,然后才能下载。打开插件市场,秋云 ucharts echarts 高性能跨全端图表组件 选择 “下载插件并倒入HBuilderX”,然后观看广告之后,打开并导入到 HBuilderX 之后,就会出现一个 UniApp .

10.2.直接使用 qiuyun 这个插件进行开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<template>
<view class="charts-box">
<qiun-data-charts type="column" :chartData="stateData.chartData" :opts="opts" />
</view>
</template>

<script setup>
import { onMounted, reactive } from "vue";
import { getRfdwd } from "@/api/index.js";

/**
* 配置内容
*/
const opts=reactive({
padding: [20,40,0,10],
background:"rgba(0,0,0,0)",
tile:{
name:"面积年增长"
},
})

/**
* 统计数据
*/
const stateData = reactive({
yearData: ['2016', '2017', '2018', '2019', '2020'], // 年份数据
mjData: [1000, 3000, 800, 2600, 4700], // 面积数据
zzlData: [100, 300, 80, 260, 500], // 增长率数据
mjYAxis: {
min: 0,
max: 4700,
}, // 面积的轴配置
zzlYAxis: {
min: 0,
max: 500,
}, // 增长率的轴配置
chartData:{} // 图表数据
});

/**
* 获取数据
*/
function initData(){
getRfdwd().then(res => {
let features=res.features;
// 执行统计
statisticsFunc(features);
})
}
/**
* 获取统计数据
*/
function statisticsFunc(features) {
let count = features.length;

let mjData = {}; // 面积数据
let jc = 1; // 基准值
for (let i = count - 1; i >= 0; i--) {
let feature = features[i];
let properties = feature.properties;
let jgrq = properties.jgrq;
let year = jgrq ? jgrq.substring(0, 4) : '';
year = parseInt(year);
let mj = parseFloat(properties.rfmj);
if (year && year >= 2018 && year <= 2022) {
mjData[year] = mj;
}
if (year == 2017) {
jc = mj;
}
}
stateData.yearData = Object.keys(mjData);
let mjArray = Object.values(mjData);
stateData.mjData = mjArray;

// 配置y轴
let sortMj = mjArray.concat([]).sort(function (a, b) {
return a - b;
});
stateData.mjYAxis = {
min: sortMj[0],
max: sortMj[sortMj.length - 1],
};

// 统计增长率
let mjDataCount = stateData.mjData.length;
let zzl = [];
for (let i = 0; i < mjDataCount; i++) {
let mj = stateData.mjData[i];
let temp = Math.floor(((mj - jc) / jc) * 10000) / 100;
zzl.push(temp);
}
stateData.zzlData = zzl;

// 配置y轴
let sortZzl = zzl.concat([]).sort(function (a, b) {
return a - b;
});
stateData.zzlYAxis = {
min: sortZzl[0],
max: sortZzl[sortZzl.length - 1],
};

// 更新图表
stateData.chartData={
categories: stateData.yearData,
series: [
{
name: "面积",
data: stateData.mjData
}
]
}
}
/**
* 挂载
*/
onMounted(()=>{
initData();
})
</script>

<style lang="less" scoped>
.charts-box{
margin-top: 20rpx;
height: 900rpx;
}
</style>

问题

(1) ctx.draw is not a function
更新数据的时候,结果就是报这个错误。

【解决方案】
原先的引入为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<qiun-data-charts type="column" :chartData="chartData" :opts="opts" />

const chartData = reactive({
categories: stateData.yearData,
series: [
{
name: "面积",
data: stateData.mjData
}
]
})

// 更新图表
chartData.categories=stateData.yearData;

修改为二级引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<qiun-data-charts type="column" :chartData="chartData" :opts="opts" />

const stateData = reactive({
chartData:{} // 图表数据
})

// 更新图表
stateData.chartData={
categories: stateData.yearData,
series: [
{
name: "面积",
data: stateData.mjData
}
]
}

10.3.原生使用

脱离了HbuildX,如何使用 uCharts 呢?
(1)变细 easycom配置,在page.json中编写 easycom 配置

1
2
3
4
5
6
"easycom": {
"autoscan": true,
"custom": {
"qiun-data-charts": "@qiun/uni-ucharts/components/qiun-data-charts/qiun-data-charts.vue"
}
}

(2) 从代码仓库 拷贝文件 到 components 文件夹。将相关的代码,从代码仓库下载下来,然后复制到 src/components 文件夹下

(3)拷贝static文件夹,将仓库中的static文件夹全部的内容拷贝到自己项目的static文件夹下

(4)文件目录,最后形成的文件目录如下:

参考文章:
【1】.uCharts / uCharts 这里就是需要拷贝的文件了
【2】.uni-app中如何使用ucharts 这里有两种方法,一种是自动引入,一种是复制文件
【3】.uni-app uCharts组件
【4】.组件方式
【5】.uniapp - easycom模式(自动引入组件) 1.在pages.json配置以下参数;2.组件引入符合命名规则;3.在页面会自动引入

11.Cesium

在uniapp中只支持高德和腾讯地图,要想使用自定义的地图还是挺麻烦的,我用了一种尝试方式,就是使用web-view的方式嵌入了。
(1)创建 web-view

1
<web-view src="pages/map/index" class="map-wrap"></web-view>

(2)引入js脚本
我尝试了使用vite进行配置,但是问题还是很多,最后直接在 index.html 文件中,使用 script 标签的方式引入了 mars3d 和 mars3d-cesium.

1
2
3
4
5
6
<!--引入cesium基础lib--> 
<link href="https://unpkg.com/mars3d-cesium@latest/Build/Cesium/Widgets/widgets.css" rel="stylesheet" type="text/css" />
<script src="https://unpkg.com/mars3d-cesium@latest/Build/Cesium/Cesium.js" type="text/javascript" ></script>
<!--引入mars3d库lib-->
<link href="https://unpkg.com/mars3d@latest/dist/mars3d.css" rel="stylesheet" type="text/css" />
<script src="https://unpkg.com/mars3d@latest/dist/mars3d.js" type="text/javascript" ></script>

(3)创建地图

(4)问题
这种方式带来的问题就是,无法在小程序和app等项目中使用了,因为小程序不支持引入外部 script 的链接。

问题

(1) Unbalanced delimiter found in string
我在使用 Mars3d 这个 npm 包的时候遇到了这个问题。

【解决方案】
我查看了这个报错的文件,然后查找了 “// #ifdef” 这个字段,发现真的有这个东西。把这个 #ifdef 删除就好了。

1
#endif // SHOW_REFLECTIVE_OCEAN

(2) 静态资源404

【解决方案】
就是将 mars3d-cesium 复制到 static/mars3d-cesium 目录,然后修改vite.config.js,通过 cesiumRunPath 定义 Cesium 的路径。

1
2
3
mars3dPlugin({
cesiumRunPath:"static/mars3d-cesium"
})

(3) define is not defined

【解决方案】
这个问题暂时没有解决,应该就是在vite中使用require语法导致的。

12.defineAsyncComponent

在使用微信小程序调试的时候,出现了 defineAsyncComponent 不支持的问题。

参考文章:
1.异步加载组件 defineAsyncComponent 整理 vue的异步加载组件函数,仅支持在H5端。不支持APP、微信小程序。

13.路由守卫

当某些页面需要登录,进入之前需要判断是否登录,如果没有登录则跳转到登录页。可以封装公共方法或混入实现,但是不太优雅,这时使用路由守卫实在是太方便了!幸好,插件uni-simple-router给我们提供了

14.地图组件

15.npm安装

参考文章:
【1】.环境安装
【2】.uni cli

16.设置相对路径

项目打包之后,配置相对目录,特别是在命令行的情况下。
(1)配置mainfast.json

1
2
3
4
5
6
"h5":{
"publicPath":"./",
"router":{
"base":"./"
}
}

(2) package.json
package.json 打包时配置输出的目录 outDir

1
"build": "uni build --mode production --outDir build",
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。