Remote access: Gain control of infected machines remotely.
Keylogging: Record keystrokes on the target system.
File management: Upload, download, and delete files on the target system.
System surveillance: Capture screenshots, record audio/video, and access webcam.
Persistence: Establish persistence on the target system to maintain access.
Remote shell: Execute commands on the target system.
The code has been optimized for connectivity, redundancy, remote text-based control, and resistance against reports/DMCA claims.
RunPE, UAC bypass, extremely strong antivirus evasion
All sensitive files are added to the antivirus exclusion list to ensure long-term stability
The downloader and binder are both 100% Fully Undetectable
The domain and ports are very resilient and cannot be taken down by reports or DMCA
We commit to delivering the full source code, along with complete setup support and step-by-step video instructions. Our team will train you thoroughly so you fully understand how to use the software and how its system operates.
In addition, we provide 24/7 updates and technical support. Our support team is always available to answer your questions and assist you whenever needed.
Regarding payment, it will be processed in stages based on project milestones, ensuring transparency and trust for both sides.
If you are new to this field, don’t worry — we will always be here to explain everything clearly and help you throughout the entire process.
We will guide you how to spread infection professionally.
I want to sell the full source for 5k USD, and I can use Ultraview for a preview
Contact me via Telegram at @sell_bot_FUD
video demo
https://www.youtube.com/watch?v=Ot-kG_MMioo
把黑客说的界面截图给 gemini 让它用 vue 实现一下,一次就 ok 了,效果还相当可以啊
这是用 wails3 打包的桌面端的结果图
代码我开源在了 https://gitee.com/tiangao/openht
公司不能上传到 github , 回头再同步了
要是有人有兴趣,下一步打算用 github 和 gitee 的 issue api 来接上实现内容流,服务器都省了
最近有需求在内网上做一个平台,做一些流程管理方面的工作,平台不大,用的人也不会很多,稳定,方便部署和维护就好。应该用什么样的方案呢?我有一些 Android 开发和 Python、Go 的背景,但是没做过前后端开发。
各位大佬们 鄙人近期开发了一套 Telegram Bot 框架 我的目标是搞一套类似 QQ机器人框架的这种 目前实现了 TUI 界面 和 一部分的插件功能 现在遇到了一个困难 就是 如何实现 读取插件内的 Token 然后进行网络请求 把数据解析然后发送给该 Token 所属插件的对应事件函数
大概就像 QQ 机器人框架的 易语言 SDK一样 每个事件下方可以实现该事件的具体功能 例如:
// SDK
void OnSendMessageEvent(){
这里来实现接收到文本消息后的具体操作
}
void OnSendStickerEvent(){
这是实现接收到贴纸消息后的具体操作
}
本项目完全开源,开源地址: https://github.com/OasisPioneer/StyxTelegramBotFramework 如果可以请大佬们点个star
你是否也在寻找一个真正免费的 AI 生图平台?市面上大多数 AI 图像生成工具如 Midjourney、Stable Diffusion 在线版、DALL-E、Flux1.ai 都需要注册账号或付费订阅,门槛太高。作为一个经常需要快速生成图片的设计师,我深知这种困扰。
-
有没有免费的 AI 生图工具?
-
哪个在线图像生成器不需要注册?
-
无需注册 AI 绘图工具有哪些推荐?
-
如何免费使用 AI 生成图片?
-
Flux Krea 免费使用是真的吗?
经过长期使用,也因为手上有几张闲着的显卡(如果生图很慢,可能是因为我在打游戏),我开发了一个完全免费的 AI 图像生成平台——Eye Dance,它彻底解决了传统 AI 生图工具的问题:无需注册、无需付费、无需复杂设置,直接输入文字描述就能生成高质量图片。
什么是 Eye Dance
EyeDance.net 是我打造的一个完全免费的 AI 生图平台,支持多种先进的 AI 模型。平台集成了 Flux Krea 免费使用、Flux Kontext 和 Stable Diffusion 在线版三大模型,为用户提供多样化的图像生成选择。
Flux Krea 免费使用版本擅长生成写实风格的高质量图片,细节表现力极强,特别适合产品展示、人像摄影等场景。Flux Kontext 在理解复杂场景描述方面表现出色,能够生成具有丰富背景和故事性的图像。Stable Diffusion 在线版则在艺术风格和创意表达上有着独特优势,支持多种艺术风格和创意概念。
恰好因为手上有几张闲着的显卡,我决定将这些计算资源投入到为更多人提供免费 AI 生图服务中。Eye Dance 的核心特性是:无需注册 AI 绘图、无需付费、无需复杂设置,直接输入文字描述就能生成高质量图片。
你不需要注册任何账号,不需要绑定支付方式,不需要学习复杂的参数设置,更不用担心使用次数限制。这种零门槛的设计让任何人都能立即开始使用这个在线图像生成器。
快速上手
-
打开网站 eyedance.net,直接进入生成页面
-
在输入框中描述你想要的图片
-
点击生成按钮,等待几秒钟
-
下载生成的图片
就是这么简单。没有注册流程,没有付费弹窗,没有复杂的参数调整。
实际使用场景
作为设计师,我经常需要快速生成概念图来展示想法。以前需要花时间注册平台、学习参数,现在只需要几秒钟就能得到结果。写文章、做视频需要配图时,Eye Dance 这个免费 AI 生图工具能快速生成符合主题的图片,不需要在版权图片库中大海捞针。
关于免费
很多人会问:为什么 Eye Dance 能完全免费?答案很简单:我恰好拥有几张显著的显卡,这些强大的计算资源让我能够为用户提供高质量的免费服务。我相信 AI 技术应该普惠大众,通过优化技术架构和运营成本,我们能够为用户提供高质量的免费 AI 生图服务。
后续可能会付费吗?可能会付费,但会一直提供免费的使用给大家。我们计划推出付费的高级功能,比如更高的并发限制、优先队列、更多模型选择等,但基础功能将始终保持免费。这样既能保证平台的可持续发展,又能让更多人享受到 AI 图像生成工具推荐的便利。
开始使用
访问 https://eyedance.net 即可开始使用。不需要注册,不需要付费,只需要你的创意。
立即体验免费 AI 生图:
-
🎨 支持多种艺术风格:写实、动漫、油画、数字艺术
-
🚀 快速生成:几秒钟即可获得高质量图片
-
🔒 隐私保护:提示词和图片不会被存储
-
💰 完全免费:无需注册 AI 绘图、无需付费、无使用限制
-
🌟 Flux Krea 免费使用:体验最先进的图像生成技术
-
📱 在线图像生成器:随时随地,打开即用
Eye Dance 将继续优化模型性能,增加更多风格选项,并计划支持更多语言。我的目标是成为最易用、最强大的 AI 图像生成工具推荐平台。
曾经困扰我的 AI 生图问题,在 Eye Dance 这里得到了完美解决。如果你也厌倦了复杂的注册流程和付费门槛,不妨试试这个真正为用户着想的免费 AI 生图产品。
还记得刚注册黑客说账号的时候,每天都回来签到,还经常发些小帖子,一两年过去,只剩偶尔几个月来一次了。但是即使隔几个月来一次,有时进站之后站顶的文章仍是我数十日前发布的,越发感觉荒凉了。
还记得最初我是以找一个功能丰富的技术分享与讨论网站的心态了解到的黑客说,当时还是非常小一个网站,但是ui风格一下就戳中了我。记得当时每日都有十数至数十个新用户到来,我当时以为这个站点会越来越活跃,可如今看到本站虽有数千用户却只有数月才有的寥寥几个帖子,不仅有些感叹。
还记得刚入站时,我在站内认识好几个志趣相投的朋友,可如今我们都已经不再黑客说上交流,转而通过在GitHub上相互欣赏、协助维护各自的开源项目,或微信、qq等方式交流。
哎,脑子想到哪里就说到那里,不知不觉也说了不少废话了。
总之,感谢站长开发并维护了这样一个给我印象颇深的优秀站点。祝愿黑客说越来越好!
技术栈
Appgallery connect
开发准备
上一节我们实现了自定义标题栏和商品详情的数据接收,我们已经拿到了想要的数据,这一节我们要丰富商品详情页的内容。商品详情页面我们需要展示的是商品的各个属性参数、商品的图片、商品规格、活动详情等
功能分析
商品详情页面的结构需要我们去用比较多的布局去处理,首先因为商品详情页面对的数据足够多,需要他能够实现滚动查看信息,然后我们需要在底部固定加入购物车和立即购买按钮,并且我们加购之后,我们也要在页面中即使响应购物车中的商品数。同时给到用户便捷跳转到购物车的按钮
代码实现
我们先进行数据的填充,因为上一节我们已经接收到数据,所以我们直接吧数据打印到text上,对着数据进行填充,同时还能帮我们暂时丰富一下页面内容,查看滑动的效果,页面完善之后我们再去删掉即可
Text(JSON.stringify(this.productParams))
.fontColor(Color.Black)
然后我们根据设计的样式进行数据填充,要注意滚动和底部布局的固定,挑选合适的布局容器
Stack({alignContent:Alignment.Bottom}){
Scroll(this.scroller){
Column() {
CommonTopBar({ title: "商品详情", alpha: 0, titleAlignment: TextAlign.Center ,backButton:true})
Image(this.productParams.url)
.width('100%')
.height(300)
Text(JSON.stringify(this.productParams))
.fontColor(Color.Black)
Column({space:10}){
Row(){
if (this.productParams.promotion_spread_price>0){
Text(){
Span("¥")
.fontSize(14)
.fontColor(Color.Red)
Span(this.productParams.promotion_spread_price+"")
.fontSize(20)
.fontColor(Color.Red)
}
}else {
Text(){
Span("¥")
.fontSize(14)
.fontColor(Color.Red)
Span(this.productParams.price+"")
.fontSize(20)
.fontColor(Color.Red)
}
}
Text("¥"+this.productParams.original_price+"")
.fontColor('#999')
.decoration({
type: TextDecorationType.LineThrough,
color: Color.Gray
})
.fontSize(16)
.margin({left:10})
if (this.productParams.promotion_spread_price>0){
Row(){
Text("每件立减"+(this.productParams.price-this.productParams.promotion_spread_price)+"元")
.fontSize(14)
.borderRadius(20)
.backgroundColor("#FB424C")
.padding(3)
.textAlign(TextAlign.Center)
Text("每人限购"+this.productParams.max_loop_amount+"件")
.margin({left:5})
.fontSize(14)
.borderRadius(20)
.backgroundColor("#FB424C")
.padding(3)
.textAlign(TextAlign.Center)
}
.padding({top:2,bottom:2,left:10})
}
}
.padding(10)
if (this.productParams.promotion_spread_price>0){
Text(this.productParams.endTime)
.fontSize(14)
.borderRadius(20)
.backgroundColor("#FB424C")
.padding(3)
.margin({left:10})
.textAlign(TextAlign.Center)
}
Text(this.productParams.name)
.fontSize(20)
.fontColor(Color.Black)
.margin({left:10})
.fontWeight(FontWeight.Bold)
Text(this.productParams.text_message)
.fontSize(14)
.fontColor(Color.Black)
.margin({left:10})
Row(){
Text()
Text("销量 "+this.productParams.sales_volume)
.fontSize(14)
.fontColor(Color.Black)
}
.padding(10)
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
Divider().width('100%')
.height(5).backgroundColor("#f7f7f7")
Row(){
Text("发货")
.fontColor(Color.Gray)
.fontSize(14)
Text(this.productParams.delivery_time+"")
.fontSize(14)
.margin({left:20})
.fontColor(Color.Black)
}
.padding(10)
.width('100%')
.justifyContent(FlexAlign.Start)
Divider().width('100%')
.height(5).backgroundColor("#f7f7f7")
Row(){
Text("参数")
.fontColor(Color.Gray)
.fontSize(14)
Text("储藏条件:")
.margin({left:20})
.fontSize(14)
.fontColor(Color.Black)
Text(this.productParams.parameter)
.fontSize(14)
.fontColor(Color.Black)
}
.padding(10)
.width('100%')
.justifyContent(FlexAlign.Start)
Divider().width('100%')
.height(5).backgroundColor("#f7f7f7")
Row(){
Text("规格")
.fontColor(Color.Gray)
.fontSize(14)
Column(){
Text("请选择规格")
}
}
.padding(10)
.width('100%')
.justifyContent(FlexAlign.Start)
Divider().width('100%')
.height(5).backgroundColor("#f7f7f7")
}
.alignItems(HorizontalAlign.Start)
}
.alignItems(HorizontalAlign.Start)
.backgroundColor(Color.White)
}
.padding({bottom:80})
.height('100%')
.width('100%')
Row(){
Image($r('app.media.product_details_cart'))
.width(35)
.height(35)
.objectFit(ImageFit.Contain)
Blank()
Text("加入购物车")
.padding(10)
.width(100)
.textAlign(TextAlign.Center)
.backgroundColor("#FCDB29")
.fontColor(Color.White)
.borderRadius({topLeft:15,bottomLeft:15})
Text(" 立即购买 ")
.padding(10)
.textAlign(TextAlign.Center)
.width(100)
.backgroundColor(Color.Red)
.fontColor(Color.White)
.borderRadius({topRight:15,bottomRight:15})
}
.padding(15)
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.backgroundColor(Color.White)
}
.backgroundColor(Color.White)
到这里我们的商品详情页面的内容已经比较完善了
技术栈
Appgallery connect
开发准备
上一节我们实现了顶部toolbar的地址选择,会员码展示,首页的静态页面就先告一段落,这节我们来实现商品列表item的点击传值、自定义标题栏。
功能分析
1.自定义标题栏
当我们进入二级三级页面的时候,就需要向用户介绍我们当前的页面信息,标题栏很好的实现了这个效果,并且进入的页面级别过多,也要给用户一个可点击的退出按钮。当然了,有些页面是不需要有返回按钮的,这里我们还要兼顾通用性。
2.页面间传值
页面之前的数据传递,是app中比较常见也是比较重要的知识点,这里我们通过点击列表的条目进行数据的传递,然后在详情页进行数据的接收
代码实现
自定义标题栏
import router from '@ohos.router'
@Component
export struct CommonTopBar {
@Prop title: string
@Prop alpha: number
private titleAlignment: TextAlign = TextAlign.Center
private backButton: boolean = true
private onBackClick?: () => void
build() {
Column() {
Blank()
.backgroundColor(Color.Red)
.opacity(this.alpha)
Stack({ alignContent: Alignment.Start }) {
Stack()
.height(50)
.width("100%")
.opacity(this.alpha)
.backgroundColor(Color.Red)
Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
Text(this.title)
.flexGrow(1)
.textAlign(this.titleAlignment)
.fontSize(18)
.fontColor(Color.Black)
.align(Alignment.Center)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.height(50)
.margin({ left: 50, right: 50 })
.alignSelf(ItemAlign.Center)
if (this.backButton) {
Stack() {
Image($r('app.media.ic_back'))
.height(16)
.width(12)
.objectFit(ImageFit.Contain)
.align(Alignment.Center)
}
.onClick(() => {
this.onBackClick?.()
router.back();
})
.height(50)
.width(50)
}
}
.height(50)
.width("100%")
Divider().strokeWidth(0.5).color("#E6E6E6")
}.backgroundColor(Color.White)
.height(51)
}
}
在标题栏中我们使用了一些逻辑判断,并且设置标题是外部传入的,而且还预留了一个事件的回调,这能让我们的标题栏更加的灵活
页面间传值
首先我们需要创建一个商品详情页的页面,然后把我们的自定义标题栏引入进去
import { CommonTopBar } from '../widget/CommonTopBar';
@Entry
@Component
struct ProductDetailsPage {
build() {
Column() {
CommonTopBar({ title: "商品详情", alpha: 0, titleAlignment: TextAlign.Center ,backButton:true})
}
.height('100%')
.width('100%')
}
}
然后在商品流的点击事件里使用router
.onClick(() => {
router.pushUrl({
url: 'pages/component/ProductDetailsPage',
params: item
}, (err) => {
if (err) {
console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`);
return;
}
console.info('Invoke pushUrl succeeded.');
});
})
这里我们把整个item的信息都传递过去,方便我们使用
接收
@State receivedParams: HomeProductList = {} as HomeProductList;
aboutToAppear(): void {
let order= router.getParams() as HomeProductList;
console.info('Received params:',order);
}
在页面上我们先展示出来
Text(JSON.stringify(this.receivedParams))
.fontColor(Color.Black)
到这里我们就实现了本节的内容了,下一节我们将要丰富商品详情页的内容
技术栈
Appgallery connect
开发准备
上一节我们实现了商品流标的创建,数据的填充和展示,并且在商品信息表中添加了许多我们后去需要使用到的参数。让我们的首页功能更加的丰富,截至目前首页板块可以说是完成了百分之五十了,跟展示有关的基本都已完成,接下来就是我们对业务逻辑的完善,当然了我们的首页内容还缺少很多,这一节我们来把顶部toolbar的地址选择,会员码展示实现一下。
功能分析
1.地址选择
地址选择我们需要实现的是省市区街道的选择,当我们点击街道信息后,根据区域的不同,我们可能会调整首页相应的活动板块修改,以及不同模块的展示,比如我们的新人领券活动,我们仅在A区域开展活动,当我们切换的B区域就会关闭相应的功能展示。同时我们下次登陆需要加载上一次选中的地址,要实现这个功能我们还需要把地址信息存储到本地。
2.会员码
会员码这个就比较的简单,我们只需要把条形码跟二维码结合用户的id生成,(因为暂时没有登陆功能,所以我们要模拟一下)在进入页面的时候把条形码加载到页面上即可。
代码实现
地址选择
因为鸿蒙中是自带这个组建的,所以我们直接在点击事件中去调用即可
let districtSelectOptions: sceneMap.DistrictSelectOptions= {
countryCode: "CN",
subWindowEnabled: false
};
sceneMap.selectDistrict(getContext(this), districtSelectOptions).then((data) => {
if (data.districts.length>5){
this.locationName=data.districts[5].name!
}else {
this.locationName=data.districts[4].name!
}
console.info("SelectDistrict", "Succeeded in selecting district."+data);
}).catch((err: BusinessError) => {
});
然后我们执行一下代码拉起地区选择的页面
然后我们实现会员码页面,这个页面就是一个一维码跟二维码的展示
因为系统不支持直接生成一维码,所以我们用到scankit ,二维码用原生
import { scanCore, generateBarcode } from '@kit.ScanKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { image } from '@kit.ImageKit';
@Entry
@Component
struct QRCodePage {
@State content: string = '1122334455';
@State pixelMap: image.PixelMap | undefined = undefined
aboutToAppear(): void {
this.pixelMap = undefined;
let options: generateBarcode.CreateOptions = {
scanType: scanCore.ScanType.CODE39_CODE,
height:200,
width: 400
}
try {
generateBarcode.createBarcode(this.content, options).then((pixelMap: image.PixelMap) => {
this.pixelMap = pixelMap;
}).catch((error: BusinessError) => {
})
} catch (error) {
}
}
build() {
Column() {
Column(){
if (this.pixelMap) {
Image(this.pixelMap).width('90%').height(70).objectFit(ImageFit.Fill)
QRCode(this.content).color(Color.Black).width('90%').height(140)
.margin({top:20})
}
}
.width('80%')
.backgroundColor("#ffffff")
.borderRadius(10)
.padding(10)
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
}
.backgroundColor("#ffeceaea")
.width('100%')
.height('100%')
}
}
这样就实现了对应的内容了
技术栈
Appgallery connect
开发准备
上一节我们实现了首页banner模块的功能,现在我们的首页还需要添加商品列表,作为一个购物类应用,商品列表是非常重要的一个模块,所以我们尽量把它设计的足够完善,参数能更好的支持我们后期复杂的逻辑,它需要有图片的展示,适配的优惠券列表,限购,立减,划线价等,但他实际的参数还要更多,因为我们的列表是比较紧凑的,更多的数据需要从点击后的商品详情页展示出来。
代码实现
创建商品表
{
"objectTypeName": "home_product_list",
"fields": [
{"fieldName": "id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "goods_list_id", "fieldType": "Integer", "notNull": true, "defaultValue": 0},
{"fieldName": "url", "fieldType": "String"},
{"fieldName": "name", "fieldType": "Text"},
{"fieldName": "price", "fieldType": "Double"},
{"fieldName": "original_price", "fieldType": "Double"},
{"fieldName": "amount", "fieldType": "Integer"},
{"fieldName": "text_message", "fieldType": "String"},
{"fieldName": "parameter", "fieldType": "String"},
{"fieldName": "delivery_time", "fieldType": "String"},
{"fieldName": "endTime", "fieldType": "String"},
{"fieldName": "sales_volume", "fieldType": "Integer"},
{"fieldName": "space_id", "fieldType": "Integer"},
{"fieldName": "max_loop_amount", "fieldType": "Integer"},
{"fieldName": "promotion_spread_price", "fieldType": "Double"},
{"fieldName": "coupon_id", "fieldType": "Integer"}
],
"indexes": [
{"indexName": "field1IndexId", "indexList": [{"fieldName":"id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
填充数据
{
"cloudDBZoneName": "default",
"objectTypeName": "home_product_list",
"objects": [
{
"id": 10,
"goods_list_id": 1,
"url": "在线图片链接",
"name": "红颜草莓",
"price": 10.5,
"original_price": 18.5,
"amount": 10,
"text_message": "特价",
"parameter": "冷藏",
"delivery_time": "付款后24小时内发货",
"endTime": "直降 | 结束时间2025年5月18日 10:00",
"sales_volume": 9812,
"space_id": 10,
"max_loop_amount": 10,
"promotion_spread_price": 5,
"coupon_id": 10
},
{
"id": 20,
"goods_list_id": 1,
"url": "在线图片链接",
"name": "麒麟瓜",
"price": 2.8,
"original_price": 5.9,
"amount": 1,
"text_message": "当季新品",
"parameter": "冷藏",
"delivery_time": "付款后24小时内发货",
"endTime": "直降 | 结束时间2025年5月18日 10:00",
"sales_volume": 9812,
"space_id": 11,
"max_loop_amount": 10,
"promotion_spread_price": 0,
"coupon_id": 10
}
]
}
我们接下来进行数据的查询
@State homeProduct:HomeProductList[]=[]//商品流数据
let databaseZone = cloudDatabase.zone('default');
let home_product=new cloudDatabase.DatabaseQuery(home_product_list);
let list7 = await databaseZone.query(home_product);
let json7 = JSON.stringify(list7)
let data7:HomeProductList[]= JSON.parse(json7)
this.homeProduct=data7
数据查出完成后,完善商品流的页面
import { HomeProductList } from "../entity/home_product_list"
@Component
@Preview
export struct WaterFlowGoods {
@Link goodsList: Array<HomeProductList>
@State columns: number = 2
build() {
WaterFlow() {
ForEach(this.goodsList, (item:HomeProductList, index) => {
FlowItem() {
Column() {
Image(item.url)
.width('100%')
.aspectRatio(1)
.objectFit(ImageFit.Cover)
.borderRadius({topLeft:10,topRight:10})
Column() {
Text(item.name)
.fontSize(16)
.fontColor('#333')
.margin({ bottom: 4 })
Text(item.text_message)
.fontSize(12)
.fontColor('#666')
.margin({ bottom: 8 })
Text("最高立减"+item.promotion_spread_price)
.fontSize(12)
.fontColor('#ffffff')
.visibility(item.promotion_spread_price>0?Visibility.Visible:Visibility.None)
.margin({ bottom: 8 })
.padding({left:5,right:5,top:2,bottom:2})
.linearGradient({
angle:90,
colors: [[0xff0000, 0], [0xff6666, 0.2], [0xff6666, 1]]
})
Row(){
Text("限购")
.width(40)
.fontSize(12)
.borderRadius(20)
.backgroundColor("#FB424C")
.padding(3)
.textAlign(TextAlign.Center)
Text("每人限购"+item.max_loop_amount+"件")
.margin({left:5})
.fontSize(12)
.fontColor("#FB424C")
}
.borderRadius(20)
.padding({top:2,bottom:2,right:10})
.backgroundColor("#FEE3E3")
.visibility(item.amount>0?Visibility.Visible:Visibility.None)
Row() {
Text(){
Span("¥")
.fontColor(Color.Red)
.fontSize(14)
Span(String(item.price))
.fontSize(16)
.fontColor(Color.Red)
}
Text(String(item.original_price))
.fontSize(12)
.fontColor('#999')
.decoration({
type: TextDecorationType.LineThrough,
color: Color.Gray
})
.margin({left:10})
Blank()
Column() {
Image($r('app.media.cart'))
.width(20)
.height(20)
}
.justifyContent(FlexAlign.Center)
.width(36)
.height(36)
.backgroundColor("#ff2bd2fa")
.borderRadius(18)
}
.margin({top:10})
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
}
.alignItems(HorizontalAlign.Start)
.padding(12)
}
.backgroundColor(Color.White)
.borderRadius(12)
.onClick(() => {
})
}
.margin({ bottom: 12 })
})
}
.padding(10)
.columnsTemplate('1fr 1fr')
.columnsGap(12)
.onAreaChange((oldVal, newVal) => {
this.columns = newVal.width > 600 ? 2 : 1
})
}
}
然后在首页调用,传入参数
WaterFlowGoods({goodsList:this.homeProduct})
到这里我们就实现了首页商品列表的内容
技术栈
Appgallery connect
开发准备
上一篇文章中我们实现了项目端云一体化首页商品活动入口列表,现在我们还差一个banner的模块,banner模块不仅可以用于展示一些信息,还可以在点击之后进行,跳转,弹窗,升级提示,信息提示等作用,我们直接坐的完善一些,因为我们事先在banner表中添加了action,我们通过这个action的值来进行对应的处理,同时通过islogin字段来判断是否需要登陆操作
代码实现
创建banner表
{
"objectTypeName": "home_banner",
"fields": [
{"fieldName": "id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "banner_id", "fieldType": "Integer", "notNull": true, "defaultValue": 0},
{"fieldName": "url", "fieldType": "String"},
{"fieldName": "is_login", "fieldType": "Boolean"},
{"fieldName": "router", "fieldType": "Boolean"},
{"fieldName": "action_id", "fieldType": "Integer"},
{"fieldName": "action", "fieldType": "String"}
],
"indexes": [
{"indexName": "banner_id", "indexList": [{"fieldName":"banner_id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
填充数据
banner
{
"cloudDBZoneName": "default",
"objectTypeName": "home_banner",
"objects": [
{
"id": 10,
"banner_id": 1,
"url": "在线图片链接",
"is_login": true,
"router": "",
"action_id": 10,
"action": "toast"
},
{
"id": 20,
"banner_id": 0,
"url": "在线图片链接",
"is_login": false,
"router": "",
"action_id": 20,
"action": "dialog"
}
]
}
由于我们缺少banner相关的内容,所以我们还需要创建一个banner的页面
import { HomeBanner } from "../entity/HomeBanner"
import showToast from "../utils/ToastUtils"
@Component
@Preview
export struct HomeBannerPage {
//数据源
@Link bannerList:HomeBanner[]
//tabs 当前数据源的下标
@State swpIndex:number=1
build() {
Column() {
Swiper(){
ForEach(this.bannerList, (item: HomeBanner) => {
Image(item.url)
.width('100%')
.height(130)
.borderRadius(10)
.onClick(()=>{
if (item.action=='toast') {
showToast("1111")
}
if (item.action=='dialog') {
}
})
})
}
.borderRadius(10)
.loop(true)
.indicator(true)
.height(130)
.onChange((index: number) => {
this.swpIndex=index+1
})
}
.padding(10)
.margin({top:10})
}
}
我们先判断是否需要is_login,然后根据action去判断,到这里我们就实现了banner的内容
技术栈
Appgallery connect
开发准备
上一篇文章中我们实现了项目端云一体化首页部分模块动态配置,实现了对模块模块的后端控制显示和隐藏,这能让我们的app更加的灵活,也能应对更多的情况。现在我们来对配置模块进行完善,除了已有的模块以外,我们还有一些banner ,活动入口等模块,这些模块的数据并不多,所以我们也归纳到配置中去实现。并且我们在配置表中添加了一些不同的id,我们只需要根据相对应的id 去查询对应的表就可以了
代码实现
实现横幅海报,商品活动入口
创建海报横幅表
{
"objectTypeName": "home_poster",
"fields": [
{"fieldName": "id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "poster_id", "fieldType": "Integer", "notNull": true, "defaultValue": 0},
{"fieldName": "url", "fieldType": "String"},
{"fieldName": "router", "fieldType": "String"}
],
"indexes": [
{"indexName": "posterIdIndex", "indexList": [{"fieldName":"poster_id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
创建商品活动入口表
{
"objectTypeName": "home_good_center",
"fields": [
{"fieldName": "id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "good_left_id", "fieldType": "Integer", "notNull": true, "defaultValue": 0},
{"fieldName": "title", "fieldType": "String"},
{"fieldName": "url", "fieldType": "String"}
],
"indexes": [
{"indexName": "goodLeftIdIndex", "indexList": [{"fieldName":"good_left_id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
分别填充数据
海报
{
"cloudDBZoneName": "default",
"objectTypeName": "home_poster",
"objects": [
{
"id": 10,
"poster_id": 1,
"url": "在线图片链接",
"router": "string1"
}
]
}
商品活动入口
{
"cloudDBZoneName": "default",
"objectTypeName": "home_good_center",
"objects": [
{
"id": 10,
"good_left_id": 1,
"title": "生鲜严选",
"url": "在线图片链接"
},
{
"id": 20,
"good_left_id": 1,
"title": "西购新品",
"url": "在线图片链接"
},
{
"id": 30,
"good_left_id": 1,
"title": "今日推荐",
"url": "在线图片链接"
}
]
}
都填充完成后,我们把数据提交到云端,然后进行配置类的同步
接下来我们进行数据查询,因为我们在配置表中添加了id ,所以我们要查询出对应id的活动入口。
@State homeActivity:HomeActivitySetting[]=[]//首页活动配置
@State homeGoodCenter:HomeGoodCenter[]=[]//商品活动入口
let listData3 = await databaseZone.query(condition3);
let json3 = JSON.stringify(listData3)
let data3:HomeActivitySetting[]= JSON.parse(json3)
this.homeActivity=data3
hilog.error(0x0000, 'testTag', `Failed to query data, code: ${this.homeActivity}`);
let list5 = await databaseZone.query(home_good);
home_good.equalTo("good_left_id",data3[0].good_left_id);
let json5 = JSON.stringify(list5)
let data5:HomeGoodCenter[]= JSON.parse(json5)
this.homeGoodCenter=data5
hilog.error(0x0000, 'testTag', `Failed to query data, code: ${this.homeGoodCenter}`);
然后我们修改一下商品活动入口的内容
import { HomeGoodCenter } from "../entity/HomeGoodCenter"
@Component
@Preview
export struct SpecialColumn {
@Link goodInfo: HomeGoodCenter[]
build() {
Column(){
List({space:10}){
ForEach(this.goodInfo,(data:HomeGoodCenter)=>{
ListItem(){
Column(){
Text(data.title)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Black)
Blank()
Image(data.url)
.width('28%')
.height(90)
.margin({ bottom: 8 })
.objectFit(ImageFit.Cover)
}
.borderRadius(5)
.backgroundColor("#ffeedeb8")
.padding(5)
}
})
}
.listDirection(Axis.Horizontal)
}
.margin({top:10})
}
}
在首页进行调用
SpecialColumn({goodInfo:this.homeGoodCenter})
到这里我们就实现了活动配置相关的内容
技术栈
Appgallery connect
开发准备
上一篇文章中我们实现了项目端云一体化金刚区活动模块,数据也成功的从云端获取,并且我们跟ScrollBar进行关联,能够让用户直观的查看当前滑动的位置。现在我们开始继续向下,随着我们首页的内容越来越多,我们如果因为某些业务需要进行调整和下线,想隐藏和关掉某些模块,就需要每次在打包的时候进行处理,这很明显会非常的麻烦,现在我们通过一张表来对首页的整个模块进行控制,这样每次做展示的时候去进行查询当前状态来实现想要的效果。
代码实现
首先我们先创建一个表把所有的关联id 以及模块的状态字段定义一下
{
"objectTypeName": "home_activity_setting",
"fields": [
{"fieldName": "id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "poster_id", "fieldType": "Integer"},
{"fieldName": "banner_id", "fieldType": "Integer"},
{"fieldName": "good_left_id", "fieldType": "Integer"},
{"fieldName": "good_right_id", "fieldType": "Integer"},
{"fieldName": "goods_list_id", "fieldType": "Integer"},
{"fieldName": "new_people_status", "fieldType": "Boolean"},
{"fieldName": "split_layout_status", "fieldType": "Boolean"},
{"fieldName": "banner_status", "fieldType": "Boolean"},
{"fieldName": "goods_list_status", "fieldType": "Boolean"}
],
"indexes": [
{"indexName": "field1IndexId", "indexList": [{"fieldName":"id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
然后我们创建数据
{
"cloudDBZoneName": "default",
"objectTypeName": "home_activity_setting",
"objects": [
{
"id": 1,
"poster_id": 1,
"banner_id": 1,
"good_left_id": 1,
"good_right_id": 1,
"goods_list_id": 1,
"new_people_status": true,
"split_layout_status": true,
"banner_status": true,
"goods_list_status": true
}
]
}
现在进行实体类的创建
export class HomeActivitySetting {
id: number;
poster_id: number;
banner_id: number;
good_left_id: number;
good_right_id: number;
goods_list_id: number;
new_people_status: boolean;
split_layout_status: boolean;
banner_status: boolean;
goods_list_status: boolean;
constructor() {
}
getFieldTypeMap(): Map<string, string> {
let fieldTypeMap = new Map<string, string>();
fieldTypeMap.set('id', 'Integer');
fieldTypeMap.set('poster_id', 'Integer');
fieldTypeMap.set('banner_id', 'Integer');
fieldTypeMap.set('good_left_id', 'Integer');
fieldTypeMap.set('good_right_id', 'Integer');
fieldTypeMap.set('goods_list_id', 'Integer');
fieldTypeMap.set('new_people_status', 'Boolean');
fieldTypeMap.set('split_layout_status', 'Boolean');
fieldTypeMap.set('banner_status', 'Boolean');
fieldTypeMap.set('goods_list_status', 'Boolean');
return fieldTypeMap;
}
getClassName(): string {
return 'home_activity_setting';
}
getPrimaryKeyList(): string[] {
let primaryKeyList: string[] = [];
primaryKeyList.push('id');
return primaryKeyList;
}
getIndexList(): string[] {
let indexList: string[] = [];
indexList.push('id');
return indexList;
}
getEncryptedFieldList(): string[] {
let encryptedFieldList: string[] = [];
return encryptedFieldList;
}
setId(id: number): void {
this.id = id;
}
getId(): number {
return this.id;
}
setPoster_id(poster_id: number): void {
this.poster_id = poster_id;
}
getPoster_id(): number {
return this.poster_id;
}
setBanner_id(banner_id: number): void {
this.banner_id = banner_id;
}
getBanner_id(): number {
return this.banner_id;
}
setGood_left_id(good_left_id: number): void {
this.good_left_id = good_left_id;
}
getGood_left_id(): number {
return this.good_left_id;
}
setGood_right_id(good_right_id: number): void {
this.good_right_id = good_right_id;
}
getGood_right_id(): number {
return this.good_right_id;
}
setGoods_list_id(goods_list_id: number): void {
this.goods_list_id = goods_list_id;
}
getGoods_list_id(): number {
return this.goods_list_id;
}
setNew_people_status(new_people_status: boolean): void {
this.new_people_status = new_people_status;
}
getNew_people_status(): boolean {
return this.new_people_status;
}
setSplit_layout_status(split_layout_status: boolean): void {
this.split_layout_status = split_layout_status;
}
getSplit_layout_status(): boolean {
return this.split_layout_status;
}
setBanner_status(banner_status: boolean): void {
this.banner_status = banner_status;
}
getBanner_status(): boolean {
return this.banner_status;
}
setGoods_list_status(goods_list_status: boolean): void {
this.goods_list_status = goods_list_status;
}
getGoods_list_status(): boolean {
return this.goods_list_status;
}
static parseFrom(inputObject: any): HomeActivitySetting {
let result = new HomeActivitySetting();
if (!inputObject) {
return result;
}
if (inputObject.id) {
result.id = inputObject.id;
}
if (inputObject.poster_id) {
result.poster_id = inputObject.poster_id;
}
if (inputObject.banner_id) {
result.banner_id = inputObject.banner_id;
}
if (inputObject.good_left_id) {
result.good_left_id = inputObject.good_left_id;
}
if (inputObject.good_right_id) {
result.good_right_id = inputObject.good_right_id;
}
if (inputObject.goods_list_id) {
result.goods_list_id = inputObject.goods_list_id;
}
if (inputObject.new_people_status) {
result.new_people_status = inputObject.new_people_status;
}
if (inputObject.split_layout_status) {
result.split_layout_status = inputObject.split_layout_status;
}
if (inputObject.banner_status) {
result.banner_status = inputObject.banner_status;
}
if (inputObject.goods_list_status) {
result.goods_list_status = inputObject.goods_list_status;
}
return result;
}
}
db类
import { cloudDatabase } from '@kit.CloudFoundationKit';
class home_activity_setting extends cloudDatabase.DatabaseObject {
public id: number;
public poster_id: number;
public banner_id: number;
public good_left_id: number;
public good_right_id: number;
public goods_list_id: number;
public new_people_status: boolean;
public split_layout_status: boolean;
public banner_status: boolean;
public goods_list_status: boolean;
public naturalbase_ClassName(): string {
return 'home_activity_setting';
}
}
export { home_activity_setting };
创建完成之后,记得要在开发工具中把数据提交到云端,我们把数据提交到云端后,可以看到数据提交成功并且表也已经创建完成
进行数据查询
let databaseZone = cloudDatabase.zone('default');
let condition3 = new cloudDatabase.DatabaseQuery(home_activity_setting);
let listData3 = await databaseZone.query(condition3);
let json3 = JSON.stringify(listData3)
let data3:HomeActivitySetting[]= JSON.parse(json3)
this.homeActivity=data3
} catch (err) {
hilog.error(0x0000, 'testTag', Failed to query data, code: ${err.code}, message: ${err.message});
}
数据也已经查询成功,接下来我们直接运行程序,可以看到金刚区是在显示中的,接下来我们修改金刚区的状态,再执行一下,可以看到金刚区已经不见了,到这里我们首页模块的显示隐藏配置已经实现了。
技术栈
Appgallery connect
开发准备
上一篇文章中我们实现了项目端云一体化新人专享券活动模块,数据也成功的从云端获取,现在我们开始继续向下,实现金刚区模块
功能分析
金刚区的实现我们之前已经完成了,但是数据的获取都是本地的静态数据,现在我们要获取云端的数据,实现数据的展示,同时要把滚动跟bar 关联起来,让用户能看到当前滑动到什么位置
代码实现
首先我们进行表、数据、实体、db类的创建
{
"objectTypeName": "split_layout",
"fields": [
{"fieldName": "split_id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "txt", "fieldType": "String"},
{"fieldName": "url", "fieldType": "String"},
{"fieldName": "router", "fieldType": "String"},
{"fieldName": "is_login", "fieldType": "Boolean"},
{"fieldName": "bt_state", "fieldType": "Integer"}
],
"indexes": [
{"indexName": "splitId_Index", "indexList": [{"fieldName":"split_id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
数据
{
"cloudDBZoneName": "default",
"objectTypeName": "split_layout",
"objects": [
{
"split_id": 10,
"txt": "果蔬肉禽",
"url": "在线图片链接",
"router": "string1",
"is_login": false,
"bt_state": 0
},
{
"split_id": 20,
"txt": "冷冻水产",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 30,
"txt": "乳品烘焙",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 40,
"txt": "粮油面点",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 50,
"txt": "酒水饮料",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 60,
"txt": "休闲零食",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 70,
"txt": "婴宠保健",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 80,
"txt": "美妆个护",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 90,
"txt": "纸品清洁",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 101,
"txt": "百货家电",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 102,
"txt": "家纺服饰",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
},
{
"split_id": 201,
"txt": "跨境免税",
"url": "在线图片链接",
"router": "string2",
"is_login": false,
"bt_state": 0
}
]
}
db类
import { cloudDatabase } from '@kit.CloudFoundationKit';
class split_layout extends cloudDatabase.DatabaseObject {
public split_id: number;
public txt: string;
public url: string;
public router: string;
public is_login: boolean;
public bt_state: number;
public naturalbase_ClassName(): string {
return 'split_layout';
}
}
export { split_layout };
实体类
/*
-
Copyright (c) Huawei Technologies Co., Ltd. 2020-2023. All rights reserved.
-
Generated by the CloudDB ObjectType compiler. DO NOT EDIT!
*/
export class SplitLayoutModel {
split_id: number;
txt: string;
url: string;
router: string;
is_login: boolean;
bt_state: number;
constructor() {
}
getFieldTypeMap(): Map<string, string> {
let fieldTypeMap = new Map<string, string>();
fieldTypeMap.set('split_id', 'Integer');
fieldTypeMap.set('txt', 'String');
fieldTypeMap.set('url', 'String');
fieldTypeMap.set('router', 'String');
fieldTypeMap.set('is_login', 'Boolean');
fieldTypeMap.set('bt_state', 'Integer');
return fieldTypeMap;
}
getClassName(): string {
return 'split_layout';
}
getPrimaryKeyList(): string[] {
let primaryKeyList: string[] = [];
primaryKeyList.push('split_id');
return primaryKeyList;
}
getIndexList(): string[] {
let indexList: string[] = [];
return indexList;
}
getEncryptedFieldList(): string[] {
let encryptedFieldList: string[] = [];
return encryptedFieldList;
}
setSplit_id(split_id: number): void {
this.split_id = split_id;
}
getSplit_id(): number {
return this.split_id;
}
setTxt(txt: string): void {
this.txt = txt;
}
getTxt(): string {
return this.txt;
}
setUrl(url: string): void {
this.url = url;
}
getUrl(): string {
return this.url;
}
setRouter(router: string): void {
this.router = router;
}
getRouter(): string {
return this.router;
}
setIs_login(is_login: boolean): void {
this.is_login = is_login;
}
getIs_login(): boolean {
return this.is_login;
}
setBt_state(bt_state: number): void {
this.bt_state = bt_state;
}
getBt_state(): number {
return this.bt_state;
}
static parseFrom(inputObject: any): SplitLayoutModel {
let result = new SplitLayoutModel();
if (!inputObject) {
return result;
}
if (inputObject.split_id) {
result.split_id = inputObject.split_id;
}
if (inputObject.txt) {
result.txt = inputObject.txt;
}
if (inputObject.url) {
result.url = inputObject.url;
}
if (inputObject.router) {
result.router = inputObject.router;
}
if (inputObject.is_login) {
result.is_login = inputObject.is_login;
}
if (inputObject.bt_state) {
result.bt_state = inputObject.bt_state;
}
return result;
}
}
然后把这些内容同步到云端
一切都完成之后,我们进行页面逻辑的修改
import { SplitLayoutModel } from "../entity/SplitLayoutModel"
@Preview
@Component
export struct SplitLayout {
@Link listData: SplitLayoutModel[]
private scroller: Scroller = new Scroller()
build() {
Column() {
Grid(this.scroller){
ForEach(this.listData, (item:SplitLayoutModel) => {
GridItem(){
Column() {
Image(item.url)
.width(45)
.height(45)
.borderRadius(24)
.margin({ top: 5 })
Text(item.txt)
.padding(2)
.fontSize(16)
.fontColor(Color.Black)
.textAlign(TextAlign.Center)
}
}
})
}
.scrollBar(BarState.Off)
.rowsTemplate('1fr 1fr')
.rowsGap(15)
.columnsGap(10)
.height(150)
ScrollBar({ scroller: this.scroller, direction: ScrollBarDirection.Horizontal,state: BarState.Auto }) {
Text()
.width(40)
.height(10)
.borderRadius(10)
.backgroundColor('#ff34a8e5')
}
.borderRadius(5)
.margin({top:10})
.width(100)
.backgroundColor('#ededed')
}
.alignItems(HorizontalAlign.Center)
.height(190)
.width('95%')
.margin({top:20})
.backgroundColor('#ffeedeb8')
.padding(16)
.borderRadius(20)
}
}
然后在主页调用组件
先创建一个接收数据变量
@State splitList:SplitLayoutModel[]=[]
SplitLayout({listData:this.splitList})
进行数据查询和赋值
let databaseZone = cloudDatabase.zone('default');
let listData2 = await databaseZone.query(condition2);
let json2 = JSON.stringify(listData2)
let data2:SplitLayoutModel[]= JSON.parse(json2)
this.splitList=data2
到这里我们的金刚区就实现了
技术栈
Appgallery connect
开发准备
上一篇文章中我们实现了项目端云一体化的升级,我们的数据后期就要从云侧的数据库去获取了,现在我们从头开始对项目进行端云一体化的改造。我们在首页已经把新人专享券抽离为公共组件
现在我们继续进行功能开发,把这个组建的本地数据展示修改为端侧获取。
功能分析
我们把之前实现的首页功能拿出改造一下。我们在首页实现了新用户领券中心,数据结构就是 模块的标题、说明、优惠券列表,列表包含优惠券的金额、类型,同时我们还要给券添加一些其他参数,比如领取时间,领取用户等,这时候就又延伸出一个功能,当我们用户没有登录的时候,我们点击立即领取,是需要跳转到用户登录页面的。
因为云数据库不支持外键,所以我们通过多插入id字段来进行数据查询。
代码实现
首先我们进行表的创建。
home_new_people_coupon 这是首页活动模块的表
{
"objectTypeName": "home_new_people_coupon",
"fields": [
{"fieldName": "activity_id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "title", "fieldType": "String", "notNull": true, "defaultValue": 0},
{"fieldName": "msg", "fieldType": "String"},
{"fieldName": "home_coupon_activity_id", "fieldType": "Integer"},
{"fieldName": "router", "fieldType": "String"},
{"fieldName": "activity_time", "fieldType": "String"}
],
"indexes": [
{"indexName": "home_coupon_activity_id_index", "indexList": [{"fieldName":"home_coupon_activity_id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
然后我们创建对应的活动id下的优惠券列表表
coupon_info
{
"objectTypeName": "coupon_info",
"fields": [
{"fieldName": "coupon_id", "fieldType": "Integer", "notNull": true, "belongPrimaryKey": true},
{"fieldName": "home_coupon_activity_id", "fieldType": "Integer", "notNull": true, "defaultValue": 0},
{"fieldName": "price", "fieldType": "String"},
{"fieldName": "type", "fieldType": "String"},
{"fieldName": "get_time", "fieldType": "String"},
{"fieldName": "limit_amount", "fieldType": "Integer"},
{"fieldName": "txt", "fieldType": "String"},
{"fieldName": "activity_id", "fieldType": "Integer"}
],
"indexes": [
{"indexName": "couponIdIndex", "indexList": [{"fieldName":"coupon_id","sortType":"ASC"}]}
],
"permissions": [
{"role": "World", "rights": ["Read"]},
{"role": "Authenticated", "rights": ["Read", "Upsert"]},
{"role": "Creator", "rights": ["Read", "Upsert", "Delete"]},
{"role": "Administrator", "rights": ["Read", "Upsert", "Delete"]}
]
}
完成之后我们插入数据
{
"cloudDBZoneName": "default",
"objectTypeName": "home_new_people_coupon",
"objects": [
{
"activity_id": 10,
"title": "新人活动",
"msg": "前三单免运费",
"home_coupon_activity_id": 10,
"router": "路由",
"activity_time": "2025-4-3"
}
]
}
{
"cloudDBZoneName": "default",
"objectTypeName": "coupon_info",
"objects": [
{
"coupon_id": 10,
"home_coupon_activity_id": 10,
"price": "10",
"type": "新人专享",
"get_time": "2025-3-18",
"limit_amount": 30,
"txt": "string1",
"activity_id": 1
},
{
"coupon_id": 20,
"home_coupon_activity_id": 10,
"price": "string2",
"type": "string2",
"get_time": "string2",
"limit_amount": 20,
"txt": "string2",
"activity_id": 1
},
{
"coupon_id": 30,
"home_coupon_activity_id": 10,
"price": "string1",
"type": "string1",
"get_time": "string1",
"limit_amount": 10,
"txt": "string1",
"activity_id": 1
},
{
"coupon_id": 40,
"home_coupon_activity_id": 10,
"price": "string2",
"type": "string2",
"get_time": "string2",
"limit_amount": 20,
"txt": "string2",
"activity_id": 1
}
]
}
数据都插入完之后,我们把内容同步到云端数据库,然后client model 、server model 创建对应的类
都执行完之后我们就可以直接在index 页面进行数据的查询了
首先创建接收数据的对象
@State home_new_people_coupon:homeNewPeopleCoupon|null=null
@State couponList:couponInfo[]=[]
然后进行查询
let databaseZone = cloudDatabase.zone('default');
let condition = new cloudDatabase.DatabaseQuery(home_new_people_coupon);
let condition1 = new cloudDatabase.DatabaseQuery(coupon_info);
let listData = await databaseZone.query(condition);
let json = JSON.stringify(listData)
let data:homeNewPeopleCoupon= JSON.parse(json)
this.home_new_people_coupon=data;
let listData1 = await databaseZone.query(condition1);
condition1.equalTo("home_coupon_activity_id",data.home_coupon_activity_id)
let json1 = JSON.stringify(listData1)
let data1:couponInfo[]= JSON.parse(json1)
this.couponList=data1
可以看到我们的云端数据已经查出来了
我们把数据修改一下提交到云端
{
"cloudDBZoneName": "default",
"objectTypeName": "coupon_info",
"objects": [
{
"coupon_id": 10,
"home_coupon_activity_id": 10,
"price": "10",
"type": "新人专享",
"get_time": "2025-3-18",
"limit_amount": 30,
"txt": "string1",
"activity_id": 1
},
{
"coupon_id": 20,
"home_coupon_activity_id": 10,
"price": "15",
"type": "新人专享",
"get_time": "string2",
"limit_amount": 20,
"txt": "string2",
"activity_id": 1
},
{
"coupon_id": 30,
"home_coupon_activity_id": 10,
"price": "20",
"type": "新人专享",
"get_time": "string1",
"limit_amount": 10,
"txt": "string1",
"activity_id": 1
},
{
"coupon_id": 40,
"home_coupon_activity_id": 10,
"price": "30",
"type": "新人专享",
"get_time": "string2",
"limit_amount": 20,
"txt": "string2",
"activity_id": 1
}
]
}
然后修改我们之间创建的新人活动的组件
import { couponInfo } from "../entity/couponInfo"
import { homeNewPeopleCoupon } from "../entity/homeNewPeopleCoupon"
@Component
@Preview
export struct CouponComponent {
@Link home_activity:homeNewPeopleCoupon|null
@Link couponList:couponInfo[]
build() {
Column() {
Row() {
Text(this.home_activity?.title)
.fontSize(20)
.fontColor('#FF0000')
Text(this.home_activity?.msg)
.fontSize(14)
.fontColor('#888888')
.margin({left:10})
}
.width('100%')
.padding(16)
List({ space: 10 }) {
ForEach(this.couponList, (item:couponInfo) => {
ListItem() {
Column() {
Text(item.price)
.fontSize(22)
.fontColor('#FF4444')
.margin({ bottom: 8 })
Text(item.type)
.fontSize(12)
.fontColor('#FF4444')
}
.padding(10)
.backgroundColor("#ffffff")
.borderRadius(8)
}
})
}
.margin({left:50})
.listDirection(Axis.Horizontal)
.width('100%')
.height(80)
Button('立即领取', { type: ButtonType.Normal })
.width(240)
.height(40)
.backgroundColor('#FF0000')
.fontColor(Color.White)
.borderRadius(20)
.margin({ bottom: 16 })
.onClick(()=>{
console.log(`"router"`)
})
}
.backgroundColor("#fffce2be")
.width('95%')
.margin({top:20})
.borderRadius(20)
}
}
首页调用组件进行参数的传入
CouponComponent({home_activity
this.couponList})
到这里我们的新人活动就完成了
大家好!我想跟大家分享一个我(AI)开发的开源项目 - VidLoad.cc ,这是一个完全基于浏览器的视频播放器和分析工具。
✨ 核心特性
-
🔒 隐私优先 - 所有视频处理都在本地浏览器中完成,绝不上传到服务器
-
🌐 通用支持 - 支持 MP4 、WebM 、HLS(M3U8)、DASH 等多种格式
-
⚡ 实时分析 - 使用 WebAssembly FFmpeg 进行元数据提取
-
🛡️ 合规友好 - GDPR/CCPA 合规,适合敏感内容处理
🎯 适用场景
-
内容创作者:上传前质量检测,多平台格式优化
-
开发者:HLS 流调试,视频播放器集成测试
-
隐私敏感行业:医疗、法律、教育等领域的视频分析
-
企业应用:员工培训视频验证,会议录制审查
🛠️ 技术栈
-
Next.js 14 + React 18 + TypeScript
-
FFmpeg.wasm (WebAssembly)
-
HLS.js + Tailwind CSS
-
完全静态部署,支持 Cloudflare Pages
🚀 在线体验
- 🌐 立即试用: https://vidload.cc
#源码开放在 GitHub ,欢迎 Star ⭐ 和贡献!
#开源 #视频处理 #隐私保护 #WebAssembly #Next.js
HackerTalk 的后端代码有 n 年没改动了
,Java 稳如老狗,k8s 上的 docker 连续跑了 1 年多都没故障重启过(7h 那个是今天调试更新),花时间一直没有动力升级。
新项目大多数用 hono + drizzle + lambda 的技术栈了,想着借助 AI 的能力把 Java 给迁移了,也是几天的技术验证,效果非常好。效果 ⬇️
Java Spring Boot: 16461 lines
- monster: 7618
- model: 3620
- channel: 2056
- notification: 1847
- websocket: 637
- gateway: 683
Nodejs: 5486 lines
- api: 4697
- notification: 789
代码量为原来的 1/3,打包后的体积 500kb 左右,使用 AWS CDK 进行部署,40 秒就能上线一个版本,更新速度非常快。
大部份简单的 API 延迟性能没有变化(语言影响不大,主要在 db 和网络上),部份 API 重构后更加轻量,延迟降低 50-100ms。
语言迁移的原因
Java + Spring Boot 确实很好用,而且非常稳定,各种后端方案都能找到,AWS、GCP、阿里云之类的云厂商也会优先发布 Java SDK,版本质量和功能完善程度都会比其他语言的 SDK 好。
但随着 AI 发展和 Serverless 技术成熟,产品迭代的速度要更快,hono + drizzle 的出现让我觉得 ts 后端非常有戏,bun 也让我看到 js 性能是没问题的,积累一整套的 SDK 代码替代 Spring Boot 大部分常用功能。
- ts 的类型系统无敌,可以解决 Java NPE 问题。
- monorepo 管理方便,前后端可以共享大量代码。
- AWS CDK + Lambda 管理方便,上线速度超快。
- 几乎所有需要常驻的场景都有 Lambda 的方案轻量替代。
- ts 做营销方便,能够找到各种库,比如 react-email 邮件营销。
- 2025 各个云厂商的 js SDK 已经很成熟。
这些点积累下来与其说 Java 不行,不如说 js/ts 的生态太强,在未来要吃掉很大部份的后端开发,优势不止相对于 java,像 go/rust 也很难赶上 ts 的优点。
可能的问题
我用 ts 实现了一套兼容 spring sercurity 的 session 管理机制,切换到 ts 后端后可能有部份用户需要重新登录,如果有其他意外情况,欢迎评论反馈,谢谢。