在这篇文章中,我们将为您详细介绍玩转喜茶微信小程序教你如何花9.9元买3杯喜茶的内容,并且讨论关于买喜茶的小程序的相关问题。此外,我们还会涉及一些关于1-微信小程序开发(安装软件和运行第一个微信小程序
在这篇文章中,我们将为您详细介绍玩转喜茶微信小程序 教你如何花9.9元买3杯喜茶的内容,并且讨论关于买喜茶的小程序的相关问题。此外,我们还会涉及一些关于1 - 微信小程序开发 (安装软件和运行第一个微信小程序)、一杯喜茶的时间实战 Deno:Deno+MongoDB (内含项目 Demo 链接)、一杯喜茶的时间手搓Promise、上千种小程序模板,教你如何开发专属小程序的知识,以帮助您更全面地了解这个主题。
本文目录一览:- 玩转喜茶微信小程序 教你如何花9.9元买3杯喜茶(买喜茶的小程序)
- 1 - 微信小程序开发 (安装软件和运行第一个微信小程序)
- 一杯喜茶的时间实战 Deno:Deno+MongoDB (内含项目 Demo 链接)
- 一杯喜茶的时间手搓Promise
- 上千种小程序模板,教你如何开发专属小程序
玩转喜茶微信小程序 教你如何花9.9元买3杯喜茶(买喜茶的小程序)
喜茶是网红饮料,每新开一家店面都大排长龙,店里平均一杯饮料价格为三十多,高于平均价格。而现在喜茶为了推广自家星球会员,推出了优惠福利活动,用户仅需花费9.9元即可购买到3杯喜茶。有效期从即日起至2019 年2月28日。
这是喜茶的「星球会员计划」的推广活动,在「喜茶」小程序中,点击「我的」就可以看到会员计划的 Banner。在这个活动中,可用 9.9 元开通 15 天体验卡,可得到 3 张免费赠饮券以及 2 张买赠券:
生日赠饮券×1
会员开通纪念日赠饮券×1
5.12 会员日赠饮券×1
买一赠一券×1
买二赠一券×1
支付后系统会要求你设置生日,并且一经设置不可更改,心急的小伙伴可以把生日设置在近期;会员日赠饮券则是要到 5 月 12 日才可使用,至于周年券,那就是明年的事了。
2 张买赠券的有效期较短,注意及时使用。至于 9 折券、150-5 这种「蚊子腿」型的券就忽略吧。
赠饮券的使用很灵活,在小程序中选择到店自取、外卖或门店直接下单均可使用,芝士茗茶、莓莓芒芒家族、满杯水果家族、纯茶、热饮推荐、波波家族、当季限定均可兑换,基本可满足大部分人的口味。
一杯喜茶均价在 25 元左右,当季限定款等都要 30+,9.9 元 3 杯可以说是白菜价了,喜茶这波新户礼非常厚道。
1 - 微信小程序开发 (安装软件和运行第一个微信小程序)
https://developers.weixin.qq.com/miniprogram/dev/
我的
打开
上传成功后
一杯喜茶的时间实战 Deno:Deno+MongoDB (内含项目 Demo 链接)
❝本文由我朋友「前端大佬 Chess」写作,下面是 Chess 大佬对 Deno 的入门实战记录,少有的 Deno 实战文章,来感受下 Deno 带来的快感吧!
❞
自从「Deno1.0」发布以来,有关 Deno 的文章很多,大多数都是在讨论怎么安装Deno
、Deno有哪些特点
、Deno和Node有哪些异同
、Deno是不是Node的替代品
等。咱们今天不讨论这些,毕竟 Talk is cheap. Show me the code!
亲自体验一把 Deno 开发带来的快感,用 Deno 搞一个 “企业级”
应用:deno-supermarket[1],难道不香吗?
Deno 常见的一些坑
在实战之前,还是先来介绍几个我在刚接触 Deno 时遇到的小坑。
权限标志符位置的问题
我们都知道,Deno 默认是安全的,就是导致了默认情况下是不允许访问网络、读写文件等。比如有个名为 index.ts
的文件内容如下:
import { serve } from "https://deno.land/std@0.50.0/http/server.ts";
const s = serve({ port: 8000 });
console.log("http://localhost:8000/");
for await (const req of s) {
req.respond({ body: "Hello World\n" });
}
如果直接执行 deno run index.ts
,会报错:error: Uncaught PermissionDenied: network access to "0.0.0.0:8000", run again with the --allow-net flag
所以我们很自然的就会在启动命令的最后加上 --allow-net
,如下:
deno run index.ts --allow-net
但是,这样仍然会报错。查了资料才知道,--allow-net
、--allow-read
之类的标志是不可以放到文件名后面的,必须紧跟在 deno run
后面,比如,如下才是正确的:
deno run --alow-net index.ts
为什么调换了位置就不行呢?issue
上的回答是,如果 --allow-net
跟在文件名后面,是传给 JS 脚本而不是传给 Deno。想了解更多可以看这个 confused by order of cli option flags[2]。反正,记住一点就行:权限标志一定要跟在 deno run
后面!
因为我们前端同学大多数平时很少写后台,不太清楚安全的重要性,为了避免遇到各种权限问题,我建议平时在写一些练手项目时,直接用 deno run -A
来启用全部的权限。(「这只是方便调试,在生产环境中一定要慎用!」)
不稳定的 API
因为实战过程中使用了 Mongodb
,所以需要引入 Deno 的第三方模块 mongo[3],然而在启动项目会报错:error: TS2339 [ERROR]: Property ''openPlugin'' does not exist on type ''typeof Deno''.
查了一下,发现是因为 openPlugin
这个方法目前还不稳定。默认情况下,Deno 只会提供稳定的 API。如果需要开启不稳定 API,可以添加 --stable
标志。比如:
deno run -A --unstable index.ts
可能有人会问,-A
和 --unstable
的位置调换会不会有问题。这个亲测过不会有问题。只要标志符在文件名称之前就行了。
还有个问题,到底哪些是稳定 API,哪些是不稳定 API 呢?其实 Deno 官方文档已经帮我们分好类的了,入口地址分别是:
-
稳定的 API 文档 [4] -
不稳定的 API 文档 [5]
如果你怀疑 --unstable
的作用,可以使用下面的方法打印出 Deno
上的所有成员:
console.log(Object.keys(Deno).length);
使用 deno run --unstable index.ts
输出的结果是 117
,使用 deno run index.ts
输出的结果是 88
。说明稳定的 API 有 88 个,不稳定的有 29 个。
Deno 的一些使用技巧
从 Node 切换到 Deno,我们的开发思维也要随之转变。所以,我们再来看看 Deno 的一些和 Node 不一样的开发技巧。
如何管理版本
刚开始我也很疑惑:没有了 package.json
,那怎么控制各依赖的版本呀?比如,我们有 10 个文件都依赖了 mongo@0.0.6
,那每个文件都使用以下代码进行引入:
import { init, MongoClient } from "https://deno.land/x/mongo@v0.6.0/mod.ts";
可是有一天,我突然想把 0.6.0
升级到 0.7.0
,那怎么办呢?一个个文件的进行替换容易漏掉,当然也可以全局搜索批量替换。但是这种效率都不是很高。
官方给出的推荐做法是,使用 deps.ts
文件来引入远程文件,并管理版本。当然,文件名称不一定叫做 deps.ts
, 你也可以改成其他的名称。具体做法就是,把所有用到的远程依赖,都在 deps.ts
中引入,并且通过 Re-export
手段导出各依赖,然后其他文件就可以从 deps.ts
中拿到所需要的依赖了。
回到刚才说 10 个文件都依赖到 mongo
的问题,如果改成 deps.ts
文件来统一管理是这样的:
export * from "https://deno.land/x/mongo@v0.6.0/mod.ts";
然后那些需要用到 mongo 的文件,不要直接从远程引入,而是从 deps.ts
中引入,如下:
import { init, MongoClient } from "../pathTo/deps.ts";
如果需要升级的话,我们可以直接把 deps.ts
里面的 mongo
地址中的 0.6.0
改成 0.7.0
就行了。
另外,有一点和 npm
类似的是,如果没有指定版本号,即远程地址中没有指定版本,比如:
export * from "https://deno.land/x/mongo/mod.ts";
就会默认安装最新版的依赖。
如何查找一些对我有用的 Deno 库
使用 Node,可以到 npm
上查找一些库。Deno 也有类似的平台,目前分为两种库,一种是官方标准的,另外一种是第三方的。标准库可以到这里查找:Deno Standard Modules[6]。第三方库可以到这里查找:Deno Third Party Modules[7]。
实战:使用 Deno 开发一个具备增删查改的商城系统
OK,具备以上的知识点,现在可以实战了。首先,需要保证你的电脑安装了 Deno1.0。另外,由于用到了 Mongodb
,所以需要你的电脑也要安装 Mongodb
。
界面
先来看看我们的商城的界面:

麻雀虽小五脏俱全哈!具备添加商品、查询商品、删除商品、修改商品的功能。这是典型的「REST API」风格的系统。
项目结构
然后再来看看项目结构:

-
「.deno_plugins」:这是 mongo
模块所下载的动态链接库,不用关注它 -
「congig/db.ts」:这是连接 Mongodb
的相关配置文件。目前写死的端口号是27017
,如果你的Mongodb
端口不是这个,可以在这个文件里面修改 -
「controllers/goods.ts」:这是实现增删查改的逻辑代码 -
「public/index.html」:这是前端静态页面,跟 Deno 无关的,我们只需要用 Deno 来服务该目录就行 -
「deps.ts」:用来管理远程依赖库,然后 Re-export
出去给其他文件使用 -
「server.ts」:入口文件,跟我们用 express
或koa
时的入口文件app.js
类似
依赖模块的选择
因为该项目涉及到了前后端,如果使用 Node 的话,一般会选择 express
或 koa
。同样的,我们使用 Deno 也要选择对应的框架,不然 http服务
以及路由跳转
等都不是那么容易处理的。Deno 上的这类框架,比较多人 star 的是 oak[8] 和 abc[9],这里我们选择使用 abc
。
另外,因为使用 Mongodb
,所以还需要引入 mongo[10]。
结束语
好啦,对于 Deno 初体验就写到这了,具体的代码这里不打算贴出来了,有兴趣的可以前往 Github 查看。
「可以复制这个地址」:https://github.com/zhanyuzhang/deno-supermarket[11]
有问题的可以一起交流学习哈~
❝笔者因为个人原因沉寂了一段时间,这段时间没有推出新文章和维护公众号,实在感到抱歉,接下来的时间里会恢复写作状态,我们一起学习一起进步,加油
❞
Reference
deno-supermarket: https://github.com/zhanyuzhang/deno-supermarket
[2]confused by order of cli option flags: https://github.com/denoland/deno/issues/5450#issuecomment-629643310
[3]mongo: https://deno.land/x/mongo@v0.6.0/mod.ts
[4]稳定的 API 文档: https://doc.deno.land/https/github.com/denoland/deno/releases/latest/download/lib.deno.d.ts
[5]不稳定的 API 文档: https://doc.deno.land/https/raw.githubusercontent.com/denoland/deno/master/cli/js/lib.deno.unstable.d.ts
[6]Deno Standard Modules: https://deno.land/std/
[7]Deno Third Party Modules: https://deno.land/x/
[8]oak: https://deno.land/x/oak
[9]abc: https://deno.land/x/oak
[10]mongo: https://deno.land/x/oak
[11]https://github.com/zhanyuzhang/deno-supermarket: https://github.com/zhanyuzhang/deno-supermarket
结语
「❤️关注 + 点赞 + 收藏 + 评论 + 转发❤️」,原创不易,鼓励笔者创作更好的文章
「关注公众号前端巅峰
,一个专注于 CSS/JS 开发技巧的前端公众号,更多前端小干货等着你喔」
-
关注后添加 我微信
拉你进技术交流群 -
欢迎关注 前端巅峰 ,更多 「CSS/JS 开发技巧」 只在公众号推送
本文分享自微信公众号 - 前端巅峰(Java-Script-)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与 “OSC 源创计划”,欢迎正在阅读的你也加入,一起分享。
一杯喜茶的时间手搓Promise
作者:JowayYoung
仓库:Github、CodePen
博客:官网、掘金、思否、知乎
公众号:IQ前端
特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权
本文由笔者师妹LazyCurry创作,收录于笔者技术文章专栏下
前言
我们都知道,JS是单线程的,只有前一个任务结束,才能执行下一个任务。显然在浏览器上,这样执行会堵塞浏览器对DOM的渲染。所以,JS中会有很多异步操作,那JS是如何实现异步操作呢?这就要想到Promise对象了,文本先来认识Promise,再手写代码实现Promise。
认识Promise
Promise是JS解决异步编程的方法之一,其英文意思是承诺。在程序中可理解为等一段时间就会执行,等一段时间就是JS中的异步。异步是指需要比较长的时间才能执行完成的任务,例如网络请求,读取文件等。Promise是一个实例对象,可从中获取异步处理的结果。
Promise有3种状态,分别是pending
(进行中)、fulfilled
(已成功)、rejected
(已失败)。只有异步操作可改变Promise的状态,其他操作都无法改变。并且状态改变后就不会再变,只能是从pending
到fulfiled
或pending
到rejected
,这也是Promise一个比较鲜明的特点。
使用Promise
上述已说到,Promise是一个对象,那么它肯定是由其构造函数来创建。其构造函数接受一个函数作为参数,其函数的参数有2个,分别是resolve
和reject
。resolve将状态从pending变为fulfiled
,成功时调用。reject将状态从pending变为rejected
,失败时调用。
function RunPromise(num, time) {
return new Promise((resolve, reject) => {
console.log("开始执行");
if (num % 2 === 0) {
setTimeout(() => {
resolve(`偶数时调用resolve,此时num为${num}`);
}, time);
} else {
setTimeout(() => {
reject(new Error(`奇数时调用rejected,此时num为${num}`));
}, time);
}
});
}
Promise对象上有then()
和catch()
方法。then()
接收2个参数,第一个对应resolve的回调,第二个对应reject的回调。catch()
跟then()
的第二个参数一样,用来接受reject的回调,但是还有一个作用,如果在then()
中执行resolve回调时抛出异常,这个异常可能是代码定义抛出,也可能是代码错误,而这个异常会在catch()
被捕获到。
RunPromise(22, 2000)
.then(res => {
console.log("then的第一个参数执行");
console.log(res);
console.log(newres);
}, error => {
console.log("then的第二个参数执行");
console.log(error);
})
.catch(error => {
console.log("error");
console.log(error);
});
// 输出结果如下:
// 开始执行
// then的第一个参数执行
// 偶数时调用resolve,此时num为22
// error
// ReferenceError: newres is not defined
上面例子中,RunPromise()
调用resolve
,then()
的第一个参数对应回调,状态从pending
改成fulfilled
,且状态不会再改变。在then()
中,newres这个变量尚未定义,因此程序出错,其异常在catch()
被捕获。一般来说,then()
使用第一个参数即可,因为catch()
跟then()
的第二个参数一样,还能捕获到异常。
实现Promise
Promise大致已了解清楚,也知道如何使用。为了了解Promise是如何实现的,我们手写实现一个简单的Promise方法,简单地实现then()、异步处理、链式调用。用最简单的思考方法,函数是为了实现什么功能,给对应函数赋予相应的实现代码即可。以下代码均使用ES6
进行书写。
定义Promise构造函数
创建Promise对象使用new Promise((resolve, reject) => {})
,可知道Promise构造函数的参数是一个函数,我们将其定义为implement
,函数带有2个参数:resolve
,reject
,而这2个参数又可执行,所以也是一个函数。
声明完成后,需要解决状态。上述已说过,Promise有3种状态,这里不再细说,直接上代码。
// ES6声明构造函数
class MyPromise {
constructor(implement) {
this.status = "pending"; // 初始化状态为pending
this.res = null; // 成功时的值
this.error = null; // 失败时的值
const resolve = res => {
// resolve的作用只是将状态从pending转为fulfilled,并将成功时的值存在this.res
if (this.status === "pending") {
this.status = "fulfilled";
this.res = res;
}
};
const reject = error => {
// reject的作用只是将状态从pending转为rejected,并将失败时的值存在this.error
if (this.status === "pending") {
this.status = "rejected";
this.error = error;
}
};
// 程序报错时会执行reject,所以在这里加上错误捕获,直接执行reject
try {
implement(resolve, reject);
} catch (err) {
reject(err);
}
}
}
then函数
我们在使用Promise时,都知道then()
有2个参数,分别是状态为fulfilled
和rejected
时的回调函数,我们在这里将2个函数定义为onFulfilled
和onRejected
。
class MyPromise {
constructor(implement) { ... }
then(onFulfilled, onRejected) {
// 当状态为fulfilled时,调用onFulfilled并传入成功时的值
if (this.status === "fulfilled") {
onFulfilled(this.res);
}
// 当状态为rejected时,调用onRejected并传入失败时的值
if (this.status === "rejected") {
onRejected(this.error);
}
}
}
异步处理
到这里已实现了基本的代码,但是异步时会出现问题。例如,本文一开始举例使用Promise时,resolve
在setTimeout()
中使用,这时候在then()
里,状态还是pending
,那就没办法调用到onFulfilled
。所以我们先将处理函数(onFulfilled
或onRejected
)保存起来,等到then()
被调用时再使用这些处理函数。
因为Promise可定义多个then()
,所以这些处理函数用数组进行存储。实现思路:
then()
增加状态为pending的判断,在此时存储处理函数resolve
或reject
时循环调用处理函数
class MyPromise {
constructor(implement) {
this.status = "pending";
this.res = null;
this.error = null;
this.resolveCallbacks = []; // 成功时回调的处理函数
this.rejectCallbacks = []; // 失败时回调的处理函数
const resolve = res => {
if (this.status === "pending") {
this.status = "fulfilled";
this.res = res;
this.resolveCallbacks.forEach(fn => fn()); // 循环执行成功处理函数
}
};
const reject = error => {
if (this.status === "pending") {
this.status = "rejected";
this.error = error;
this.rejectCallbacks.forEach(fn => fn()); // 循环执行失败处理函数
}
};
try {
implement(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.status === "fulfilled") {
onFulfilled(this.res);
}
if (this.status === "rejected") {
onRejected(this.error);
}
// 当状态为pending时,说明这时还没有调用到resolve或reject
// 在这里把成功函数和失败函数存至相应的数组中,不做执行操作只做存储操作
if (this.status === "pending") {
this.resolveCallbacks.push(() => onFulfilled(this.res));
this.rejectCallbacks.push(() => onRejected(this.error));
}
}
}
测试一下异步功能,打印结果中,''执行resolve''是等待了2秒后打印出来的
new MyPromise((resolve, reject) => {
console.log("开始执行");
setTimeout(() => {
resolve("执行resolve");
}, 2000);
}).then(res => console.log(res));
// 输出结果如下:
// 开始执行
// 执行resolve
链式调用
到这里就已实现异步操作啦!吼吼~但是,我们都知道,Promise能定义多个then,就例如new Promise().then().then()
,这种就是链式调用。当然我们也要实现这个功能。
链式调用是指Promise在状态是fulfilled
后,又开始执行下一个Promise。要实现这个功能,我们只需要在then()
里返回Promise就好了,说起来好像是挺简单的。
then()
的实现思路:
then()
中需要返回Promise对象,我们将其命名为nextPromise
- 仍然需要判断状态,执行相应处理
onFulfilled
和onRejected
是异步调用,用setTimeout(0)
解决- 需要对
onFulfilled
和onRejected
类型做判断,并做相应返回
class MyPromise {
constructor(implement) { ... }
then(onFulfilled, onRejected) {
// 如果onRejected不是函数,就直接抛出错误
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : res => res;
onRejected = typeof onRejected === "function" ? onRejected : err => { throw err; };
const nextPromise = new MyPromise((resolve, reject) => {
if (this.status === "fulfilled") {
// 解决异步问题
setTimeout(() => {
const x = onFulfilled(this.res);
RecursionPromise(nextPromise, x, resolve, reject);
}, 0);
}
if (this.status === "rejected") {
setTimeout(() => {
const x = onRejected(this.error);
RecursionPromise(nextPromise, x, resolve, reject);
}, 0);
}
if (this.status === "pending") {
this.resolveCallbacks.push(() => {
setTimeout(() => {
const x = onFulfilled(this.res);
RecursionPromise(nextPromise, x, resolve, reject);
}, 0);
});
this.rejectCallbacks.push(() => {
setTimeout(() => {
const x = onRejected(this.error);
RecursionPromise(nextPromise, x, resolve, reject);
}, 0);
});
}
});
return nextPromise;
}
}
RecursionPromise()
用来判断then()
的返回值,以决定then()
向下传递的状态走resolve
还是reject
,实现思路:
nextPromise
与x
不能相等,否则会一直调用自己- 判断
x
的类型,如果不是函数或对象,直接resolve(x)
- 判断
x
是否拥有then()
,并且如果then()
是一个函数,那么就可执行x
的then()
,并且带有成功与失败的回调 flag
的作用是执行x
的then()
时成功与失败只能调用一次- 执行
x
的then()
,成功时继续递归解析 - 如果
then()
不是一个函数,直接resolve(x)
function RecursionPromise(nextPromise, x, resolve, reject) {
if (nextPromise === x) return false;
let flag;
if (x !== null && (typeof x === "object" || typeof x === "function")) {
try {
let then = x.then;
if (typeof then === "function") {
then.call(x, y => {
if (flag) return false;
flag = true;
// 这里说明Promise对象resolve之后的结果仍然是Promise,那么继续递归解析
RecursionPromise(nextPromise, y, resolve, reject);
}, error => {
if (flag) return false;
flag = true;
reject(error);
});
} else {
resolve(x);
}
} catch (e) {
if (flag) return false;
flag = true;
reject(e);
}
} else {
resolve(x);
}
}
总结
具有异步处理
和链式调用
的Promise已实现啦!还有一些方法在这里就不一一实现了。毕竟实现一个完整的Promise不是一篇文章就能讲完的,有兴趣的同学可自行参照Promise的功能进行解构重写,若有写得不正确的地方请各位大佬指出。公众号后台回复promise可获取本文的源码,如果是转载的文章,可关注IQ前端再回复promise即可。
写这篇文章的目的是为了给各位同学提供一个函数解构的思路,学会去分析一个函数的功能,从而解构出每一个步骤是如何执行和实现的,祝大家学习愉快,下次再见~
结语
❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更多高质量文章
关注公众号IQ前端
,一个专注于CSS/JS开发技巧的前端公众号,更多前端小干货等着你喔
- 关注后回复
资料
免费领取学习资料 - 关注后回复
进群
拉你进技术交流群 - 欢迎关注
IQ前端
,更多CSS/JS开发技巧只在公众号推送
上千种小程序模板,教你如何开发专属小程序
想自己搭建一个小程序又无从下手?那应该如何开发和运营自己的专属小程序呢?今天小编就从搭建小程序到运营小程序一体方法和工具分享给你,只要按照步骤操作,不用懂编程也能搭建和运营专属小程序!
首先找到本次讲解用到的工具,然后注册一个免费账号,就能登录到工具后台。
进入本次使用的工具平台,点击其中的【商城】选项,进入商城工具的后台中。
点击后台中的小程序商城选项,这样就可以找到本次需要使用的小程序商城模板。
在选择其中小程序模板时,只需点击一个模板,然后点击【马上替换】使用商城模板。
进入模板中,对需要修改的部分只需找到编辑按钮就能修改,例如图中编辑导航模板,在右侧的编辑栏就能进行设置。
在初次编辑和后期维护商城内的商品时,可以借助商城后台中的工具进行批量上架管理。
在对小程序模板中的基础部分完成编辑后,还可以使用左侧的编辑工具,将自己觉得需要添加功能和修改设计的商城内容进行编辑。
完成商城的搭建后,点击右上角的【保存并预览】就可以查看商城的上线效果。如图
最后扫码绑定,再将小程序商城进行上线发布就完成了。
以上就是小程序商城工具的使用教程,不用编程在线就能开发和维护小程序商城!只需几分钟就能掌握这个工具!
今天关于玩转喜茶微信小程序 教你如何花9.9元买3杯喜茶和买喜茶的小程序的讲解已经结束,谢谢您的阅读,如果想了解更多关于1 - 微信小程序开发 (安装软件和运行第一个微信小程序)、一杯喜茶的时间实战 Deno:Deno+MongoDB (内含项目 Demo 链接)、一杯喜茶的时间手搓Promise、上千种小程序模板,教你如何开发专属小程序的相关知识,请在本站搜索。
本文标签: