使用Trae开发微信小程序(智能换背景)学习

1、界面

trae生成图3种方法

1、文生图:提示词生成图

2、图生图:上传任意图片,实拍照片、截图,让trae生成

3、线稿/草图约束生成: 手绘草稿、流程图,让trae生成

本文以"文生图"举例

1.1、通过提示词生成图

在trae中输入如下:提示词

复制代码
你是一位资深设计师,你非常了解微信小程序的设计风格,拥有丰富的全栈开发经验和极高的审美,擅长设计现代风格的微信小
## 程序端界面
我的微信小程序需求是:
我要做一款智能图片换背景微信小程序,根据产品功能 1-需求分析/换背景需求.md 设计全套界面原型

## 我的要求
1、页面元素尽量高级美观,遵循移动端设计规范,注重 UI 设计细节,修图工具类轻量化精致视觉,分层光影、卡片圆角、渐变质感精细化处理
2、所有数据使用假数据,上传图片、背景模板、功能按钮、弹窗、切换标签全部可以点击交互,包含上传图片、智能抠图、背景替换、纯色 / 素材背景、微调、保存分享完整交互逻辑
3、图标统一使用 CDN 在线资源引入,不使用本地静态图标文件
4、页面文件分拆管理,每个功能子页面单独生成独立 html 文件,总入口 index.html 集中承载所有子页面实现统一预览查看
5、设备载体尺寸模拟 iPhone17 Pro,整机外层容器做大圆角真机外壳效果,高度还原真实微信小程序展示观感
6、页面顶部移除手机网络、时间、电量等系统状态栏,仅保留小程序自定义导航栏,无多余原生系统 UI 干扰
7、区分免费素材、会员高清背景两套视觉模块,增加会员解锁弹窗、广告引导弹窗完整页面交互设计
8、适配图片编辑操作逻辑,添加画布缩放、画笔擦除抠图、边缘优化、尺寸裁剪配套操作面板 UI,触控按钮尺寸符合手机单手操作标准

请按以上 8 条要求生成完整可运行的高保真原型 html 全套代码,代码放到UI目录下

生成效果图,如下:自己做时,如果哪里不合适再和trae交互,通过提示词、或截图方式让trae再修改。

2、生成项目架构设计文档

使用AI开发项目,不能只丢一句自然语言 让它生成整个项目,AI会随便乱写。需要给它约束和规范,我要让AI生成2个架构设计文档,后续让AI 按这个文档 开发。提示词如下:

bash 复制代码
根据需求文档和UI设计图,生成项目架构设计文档,包含后端架构文档和微信小程序架构文档

要求:
1.后端使用Python+Django 4 + Mysql8 + DjangoRestFramework等技术实现。

2.微信小程序端使用小程序原生框架。

3.后台管理使用django 自带admin和Simpleui美化。

4.前后端目录结构都一并生成,后端接口格式和参数和返回值格式也生成。

5.后端数据库设计:设计出项目所需表和字段(简要ER图),用户表使用Django自带的User表。

6.以列表的形式给我一个开发计划,以Markdown格式生成并输出,

7.后端文档写入到文件中: 3-架构设计 /1-项目后端架构文档.md。

8.微信小程序文档写入文件: 3-架构设计 /2-微信小程序架构文档.md。

3、python调用coze工作流

后端调用coze的工作流,所以需要python调用coze工作流的代码,如下:

python 复制代码
import requests
import json
import time
import os
from typing import Optional, Dict, Any

'''
个人访问令牌:https://www.coze.cn/open/oauth/pats
轮询配置说明:
POLL_INTERVAL: 每次查询间隔(秒)
MAX_WAIT_SECONDS: 任务最大等待总时长(秒),超时直接终止
'''

class CozeAPI:
    """Coze API客户端,用于调用Coze的换背景工作流"""
    # 轮询全局配置
    POLL_INTERVAL = 20    # 每20秒查询一次任务状态
    MAX_WAIT_SECONDS = 600 # 最长等待600秒,超时失败
    REQUEST_TIMEOUT = 15  # 单次http请求超时时间

    def __init__(self, api_key: str = None, workflow_id: str = None):
        self.api_key = api_key or ''
        self.workflow_id = workflow_id or ''
        self.base_url = 'https://api.coze.cn'
        self.upload_url = f"{self.base_url}/v1/files/upload"
        self.run_url = f"{self.base_url}/v1/workflow/run"
        self.headers = {
            'Authorization': f'Bearer {self.api_key}',
            'Content-Type': 'application/json'
        }

    def _get_save_path(self, task_id: str) -> str:
        """内部方法:生成图片保存路径(精简路径处理逻辑)"""
        result_dir = os.path.join('face_swap', 'coze_results')
        os.makedirs(result_dir, exist_ok=True)
        filename = f"result_{task_id}_{int(time.time())}.jpg"
        return os.path.join(result_dir, filename)

    def download_image(self, image_url: str, task_id: str) -> Optional[str]:
        """下载图片到本地,返回跨平台路径"""
        try:
            save_path = self._get_save_path(task_id)
            resp = requests.get(image_url, stream=True, timeout=self.REQUEST_TIMEOUT)
            resp.raise_for_status()

            with open(save_path, 'wb') as f:
                for chunk in resp.iter_content(chunk_size=8192):
                    f.write(chunk)

            print(f"图片下载成功: {save_path}")
            return save_path.replace('\\', '/')
        except Exception as e:
            print(f"图片下载失败: {str(e)}")
            return None

    def upload_file(self, file_path: str) -> str:
        """上传文件到Coze并获取文件ID"""
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")

        try:
            print(f"开始上传文件: {file_path}")
            upload_header = {'Authorization': f'Bearer {self.api_key}'}
            with open(file_path, 'rb') as f:
                files = {'file': (os.path.basename(file_path), f)}
                resp = requests.post(
                    self.upload_url,
                    headers=upload_header,
                    files=files,
                    timeout=self.REQUEST_TIMEOUT
                )

            print(f"上传响应: {resp.status_code} | {resp.text}")
            result = resp.json()

            if resp.status_code == 200 and result.get('code') == 0 and 'data' in result:
                file_id = result['data'].get('id')
                if not file_id:
                    raise Exception(f"未提取到文件ID: {result}")
                print(f"文件上传成功,ID: {file_id}")
                return file_id
            raise Exception(f"文件上传失败: {result}")
        except Exception as e:
            print(f"文件上传异常: {str(e)}")
            raise

    def run_workflow(self, source_image: str, target_image: str) -> Dict[str, Any]:
        """运行Coze换背景工作流,异步提交任务,返回task_id"""
        try:
            # 批量上传文件
            source_id, target_id = self.upload_file(source_image), self.upload_file(target_image)

            payload = {
                "workflow_id": self.workflow_id,
                "parameters": {
                    "person": json.dumps({"file_id": source_id}),
                    "background": json.dumps({"file_id": target_id}),
                    "text": "换背景"
                },
                "app_id": "",
                "is_async": True
            }

            print(f"调用工作流 {self.workflow_id}...")
            resp = requests.post(
                self.run_url,
                headers=self.headers,
                json=payload,
                timeout=self.REQUEST_TIMEOUT
            )

            if resp.status_code != 200:
                raise Exception(f"工作流调用失败: {resp.status_code} | {resp.text}")

            result = resp.json()
            if result.get('code') == 0 and (task_id := result.get('execute_id')):
                print(f"工作流调用成功,任务ID: {task_id}")
                return {
                    'code': 0,
                    'msg': 'success',
                    'data': {
                        'task_id': task_id,
                        'workflow_id': self.workflow_id,
                        'status': 'pending'
                    }
                }

            raise Exception(f"工作流调用失败: {result.get('msg')} | {result}")
        except Exception as e:
            print(f"工作流调用异常: {str(e)}")
            raise

    def get_workflow_result(self, task_id: str) -> Dict[str, Any]:
        """单次查询工作流执行结果"""
        try:
            print(f"查询任务 {task_id} 状态...")
            query_url = f"{self.base_url}/v1/workflows/{self.workflow_id}/run_histories/{task_id}"
            resp = requests.get(query_url, headers=self.headers, timeout=self.REQUEST_TIMEOUT)

            if resp.status_code != 200:
                raise Exception(f"查询失败: {resp.status_code} | {resp.text}")

            result = resp.json()
            if result.get('code') != 0:
                raise Exception(f"查询结果失败: {result.get('msg')} | {result}")

            # 取第一条任务数据
            data_list = result.get('data', [])
            if not data_list:
                return {'status': 'processing', 'task_id': task_id}
            data = data_list[0]
            exec_status = data.get('execute_status', '')

            if exec_status == 'Success':
                return self._parse_success_result(data, task_id)
            elif exec_status == 'Fail':
                return {
                    'status': 'failed',
                    'error_message': data.get('error_reason', '未知执行错误'),
                    'task_id': task_id
                }
            # 其余状态统一视为处理中
            return {'status': 'processing', 'task_id': task_id}

        except Exception as e:
            print(f"查询结果异常: {str(e)}")
            raise

    def wait_workflow_finish(self, task_id: str) -> Dict[str, Any]:
        """
        循环轮询等待任务完成
        :param task_id: 异步任务ID
        :return: 最终任务结果(成功/失败)
        """
        start_time = time.time()
        while True:
            # 判断是否超时
            elapsed = time.time() - start_time
            if elapsed >= self.MAX_WAIT_SECONDS:
                return {
                    'status': 'timeout',
                    'error_message': f"任务超时,已等待{self.MAX_WAIT_SECONDS}秒仍未完成",
                    'task_id': task_id
                }

            try:
                res = self.get_workflow_result(task_id)
                status = res.get('status')
                if status in ('success', 'failed'):
                    return res
                print(f"任务仍在处理中,已等待{round(elapsed,1)}s,{self.POLL_INTERVAL}s后重新查询...")
            except Exception as e:
                print(f"本次查询接口出错,跳过本次: {str(e)}")

            time.sleep(self.POLL_INTERVAL)

    def _parse_success_result(self, data: Dict[str, Any], task_id: str) -> Dict[str, Any]:
        """内部方法:解析成功的任务结果"""
        try:
            output = json.loads(data.get('output', '{}'))
            result_data = json.loads(output.get('Output', '{}'))
            image_url = result_data.get('output') # 原来是output,现在变成 out

            if not image_url:
                return {
                    'status': 'success',
                    'message': '任务成功但未返回图片链接',
                    'task_id': task_id
                }

            local_path = self.download_image(image_url, task_id)
            return {
                'status': 'success',
                'result_image_url': image_url,
                'local_image_path': local_path,
                'processing_time': data.get('processing_time', 0),
                'task_id': task_id
            }
        except (json.JSONDecodeError, KeyError) as e:
            print(f"解析输出JSON失败: {str(e)}")
            return {
                'status': 'success',
                'message': f'任务执行成功,但解析返回数据异常:{str(e)}',
                'task_id': task_id
            }


if __name__ == '__main__':
    try:
        coze_api = CozeAPI(api_key="你的key", workflow_id="你的workflowid")
        source_path, target_path = './person.jpg', './background.jpg'
        print("===== 开始测试Coze异步换背景工作流 =====")

        # 1. 提交异步任务
        run_result = coze_api.run_workflow(source_path, target_path)
        print(f"任务提交返回: {run_result}")

        if run_result.get('code') == 0:
            task_id = run_result['data']['task_id']
            print(f"获取任务ID: {task_id}")

            # 2. 循环轮询等待任务结束(替换原来固定sleep)
            final_result = coze_api.wait_workflow_finish(task_id)
            print("\n===== 任务最终结果 =====")
            print(final_result)

            # 3. 结果分类打印
            status = final_result.get('status')
            if status == 'success':
                print(f"\n换背景成功!")
                print(f"在线图片地址:{final_result['result_image_url']}")
                print(f"本地保存路径:{final_result['local_image_path']}")
            elif status == 'failed':
                print(f"\n任务执行失败:{final_result['error_message']}")
            elif status == 'timeout':
                print(f"\n任务等待超时:{final_result['error_message']}")
            else:
                print(f"\n未知任务状态:{status}")

    except Exception as e:
        print(f"\程序整体异常: {str(e)}")

4、使用提示词生成后端代码

4.1、提示词生成代码

bash 复制代码
根据项目需求: 和项目后端架构文档:  和前端设计图: ,生成智能换背景微信小程序后台Django的项目和代码
要求:
1.项目写入到目录4-change_background_api中。
2.生成相关表模型,写入到每个app的models中。
3.生成所有接口,并能正常调用。
4.链接数据库地址为:(需要改成你自己的)
    -host:127.0.0.1
    -port:3306
    -database:chang_background
    -user:root
    -password:123456
5.Django 后台admin使用django-simpleui美化,项目做好本地化和时区设置。
6.换脸功能对接Coze一键换背景工作流,对接方案参照代码:
	-API_KEY: 你的key
        -工作流ID: 你的workflowid

4.2、登录后台管理页

此时后端代码已经写完,trae会启动服务,也可以手动启动服务(任何不清楚 都可以 文trae),命令如下:

python manage.py runserver 0.0.0.0:8000

trae生成代码过程中,会创建超级用户,记住用户名、密码,用于登录后台管理页面。

5、微信小程序

注册账号

网址:https://mp.weixin.qq.com/

相关信息按需填写

5.1、下载微信开发者工具

trae开发微信小程序无法调试,需要使用微信开发者工具(可以调试编译)

5.2、创建微信小程序项目

云开发: 使用微信提供的后端,可以存储数据,执行代码,但是需要花钱,好处是我们自己不需要开发后端了。

5.3、目录结构介绍

1 项目主配置文件

项目主配置文件必须放到项目的根目录下,控制整个项目

-.eslintrc.js:JS 格式配置文件

  • app.js: 小程序入口文件

  • app.json:小程序的全局配置文件

  • app.wxss:小程序的全局样式

  • project.config.json 小程序的配置

  • app.js 和 app.json 文件是必须的,不能没有

2 页面文件

小程序有一个个页面,每个页面所需的文件,都存放在 pages 目录下,一个页面一个文件夹

-xx.js: 页面逻辑 js代码存放位置

-xx.wxml:页面结构 类html文件存放位置

-xx.wxss:页面样式 css存放位置

-xx.json:小页面配置

-xx.js 文件和 xx.wxml 文件是必须的,不能没有

3 组件

components: 组件

5.4、Trae用提示词开发微信小程序

提示词

bash 复制代码
根据项目需求: 和项目小程序架构文档: 和UI设计图:  生成智能换背景小程序端代码
要求:
1.我已经创建了小程序: ,你在这个基础上继续编写,无效的文件和文件夹帮我删除。
2.根据需求帮编写完小程序端代码。
3.接口参考后端项目: ,注意接口返回格式,并正常测试通过,前后端调通。
4.后端链接地址为:http://127.0.0.1:8000。
5.小程序不使用Skyline 渲染模式。
6.小程序用户未登录,显示登录页面,登录成功进入首页。

在微信开发者工具的模拟器上就能看到效果了

5.5、功能测试

此时可以在微信开发者工具上测试功能了,比如注册、登录、换背景、历史记录,可以打开调试面板,console查看日志,network查看网络请求。如果有报错,可以截图,直接丢给trae,让它修改。