11.OpenCode 生成 Vue3 前端「私文简搜」完整实操记录 + Docker 部署全过程

0 阅读8分钟

11.OpenCode 生成 Vue3 前端「私文简搜」完整实操记录 + Docker 部署全过程

前面已完成API的代码开发,现在结合OpenCode完成「私文简搜」Vue3前端开发。

一. 前端项目开发

1.1 通过OpenCode创建项目

在OpenCode中发指令(前提是打开项目不变)

分析前面代码后,在ivy-simple-api目录中创建 frontend前端项目,vue3 有登录,查询,类似百度一样的页面,调用查询函数,显示列表。首先显示的是百度一样的搜索页面,只有登录后才能进行查询,登录页面也要生成。

QQ_1783992928984.png

任务拆分分步执行,进度效果正常。

QQ_1783993076763.png

代码生成任务执行完成。

QQ_1783993181134.png

1.2 VS Code 打开代码本地调试

打开代码后执行npm i安装依赖,过程中出现多类问题逐一修复。

1.2.1 PowerShell 执行策略报错

QQ_1783993622895.png

修复命令:

Set-ExecutionPolicy RemoteSigned

QQ_1783993683314.png

1.2.2 安装项目依赖
npm i

QQ_1783993898591.png

1.2.3 启动项目报变量重名错误
npm run dev

完整报错日志:

 Failed to scan for dependencies from entries:
  E:/ivy/ivy-simple-api/frontend/index.html

  X [ERROR] The symbol "getSmsCode" has already been declared

    script:E:/ivy/ivy-simple-api/frontend/src/views/Login.vue?id=0:17:15:
      17 │ async function getSmsCode() {
         ╵                ~~~~~~~~~~

  The symbol "getSmsCode" was originally declared here:

    script:E:/ivy/ivy-simple-api/frontend/src/views/Login.vue?id=0:4:9:
      4 │ import { getSmsCode, smsVerify } from '../api'
        ╵          ~~~~~~~~~~


    at failureErrorWithLog (E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:1472:15)
    at E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:945:25
    at runOnEndCallbacks (E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:1315:45)
    at buildResponseToResult (E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:943:7)
    at E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:955:9
    at new Promise (<anonymous>)
    at requestCallbacks.on-end (E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:954:54)
    at handleRequest (E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:647:17)
    at handleIncomingPacket (E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:672:7)
    at Socket.readFromStdout (E:\ivy\ivy-simple-api\frontend\node_modules\esbuild\lib\main.js:600:7)

QQ_1783994541885.png

本地访问地址:[http://localhost:3000/]

QQ_1783994599982.png

QQ_1783994775204.png

QQ_1783994989557.png

1.2.4 页面基础信息调整

QQ_1783995078015.png OpenCode 优化指令:

项目中文名:私文简搜 英文名:DocSolo ,将ivy Search替换为中文项目名 时间改为 yyyy-MM-dd HH:mm:ss

QQ_1783995507335.png

调整后主页面展示效果:

QQ_1783995542162.png 搜索结果列表页面:

QQ_1783995610714.png

1.2.5 新增登录缓存、文档详情功能

OpenCode 指令:

保存登录信息,并显示详细页面

QQ_1783996046831.png

QQ_1783996085099.png

补充优化需求:

还有要改进的地方,手机号要加掩码,中间***,退出登录后不要进行login页面,而是回到主页面,前面还有一个功能没完成,搜索后的内容,点击进入详细页面。页面可以从目录文件获取。还有,debug模式去掉,如果code不为空,就直接显示,还有登录页面记录上次的手机号

QQ_1783996302368.png

同步配套修改后端代码:

QQ_1783996581531.png

1.2.6 登录交互 Bug 修复

在OpenCode中输入

退出后,再单击登录无法进入登录页面,但要搜索时,没有登录是可以跳入登录页面的。登录页面还是没有记录上次登录帐号

QQ_1783996887824.png

补充问题:

还是单击登录,无法跳入登录页面

QQ_1783997272251.png

修复完成后,登录跳转、手机号本地缓存功能正常。

1.2.7 文档详情页面异常问题

路由报错问题:

查看详情页面显示的是
http://localhost:3000/detail/null,导致错误

QQ_1783997522767.png

文档渲染缺陷:

页面没有按md格式显示,还有图片也要正确显示,还有高亮也要在正文中显示

QQ_1783997775414.png

详情页面基础效果:

QQ_1783997867528.png

新增需求:

还有个小问题,详情 做为新页面

QQ_1783997893471.png

1.2.8 文档图片加载异常修复

图片无法正常展示截图:

QQ_1783998131067.png

路径替换方案输入OpenCode

 图片路径可以替换成  192.168..../md-doces/111/22/xxx/images/具体的图片.png

QQ_1783998480060.png 修复后效果:Markdown 渲染、关键词高亮、内嵌图片全部正常展示

QQ_1783998755635.png

1.3 前端 API 代理统一改造

1.3.1 原始多代理配置(维护繁琐)

原始 vite.config.js 代码中api配置三个地方,要修改成一个

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,
    proxy: {
      '/auth': {
        target: 'http://192.168.55.140:8101',
        changeOrigin: true
      },
      '/md': {
        target: 'http://192.168.55.140:8101',
        changeOrigin: true
      },
      '/md-docs': {
        target: 'http://192.168.55.140:8101',
        changeOrigin: true
      }
    }
  }
})

分析代码,同时修改frontend 所有访问的接口都是/api,转发时去掉/api,这样配置就只用配置一个地方。现在搞三个地方,访问修改有点麻烦 

QQ_1784205120390.png

1.3.2 接口与 Vite 配置改造完成

改造后接口请求代码:


export function getSmsCode(mobile) {
  return api.post('/api/auth/user/getSmsCode', { mobile })
}

export function smsVerify(mobile, code, verify) {
  return api.post('/api/auth/user/smsVerify', { mobile, code, verify })
}

export function search(keywords, page = 1, pageSize = 10) {
  return api.get('/api/md/search', { params: { keywords, page, pageSize } })
}

export function getDocDetail(id) {
  return api.get('/api/md/detail', { params: { id } })
}

改造后单 /api 代理 vite 配置:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://192.168.55.140:8101',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

1.3.3 改造后本地启动验证

QQ_1784206201505.png

二. 前端 Docker 容器化部署

部署思路:基础 Dockerfile 打包,正式环境使用数据卷挂载配置与静态资源,迭代无需重新构建镜像;适配 Jenkins 自动化打包流程。

2.1 前端目录新增部署文件

新增 Dockerfile、nginx.conf,用于线上代理转发配置参考

在frontend中添加Dockerfile文件,还有nginx配置文件也放在里面,不一定现在就用,主要用于指导nginx配置。/api映射的

QQ_1784206779880.png

2.2 Dockerfile 调整

初始多阶段构建脚本:

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

不知道为什么要配置两个,我想通过jenkins来部署,就不要前面这个了。并且nginx不使用alpine版本,并能适配 Jenkins打包。

去要使用jenkins进行打包,所以前面一个不用,直接使用FROM nginx ,并增加维护者信息
#维护者信息
#MAINTAINER 黄卫华 <wayhua@126.com>

QQ_1784206992753.png

修改配置文件复制路径:

COPY nginx.conf /etc/nginx/nginx.conf

QQ_1784207129088.png

这里也埋了一个bug。

2.3 Nginx 代理配置片段

server {
    listen 80;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass http://192.168.55.140:8101/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /md-docs/ {
        proxy_pass http://192.168.55.140:8101/md-docs/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

看到这个代码,是不是vite.config.js 中也要配置md-docs

看到nginx这个代码,是不是vite.config.js 中也要配置md-docs

QQ_1784207308199.png

2.4 最终 vite.config.js(本地 + 静态资源双代理)

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://192.168.55.140:8101',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      },
      '/md-docs': {
        target: 'http://192.168.55.140:8101',
        changeOrigin: true
      }
    }
  }
})

2.5 docker-compose 编排部署

2.5.1 前端打包编译
npm run build

QQ_1784207552715.png

2.5.2 部署目录结构

在 api 目录新建 ui 文件夹,存放 dist(编译后的文件夹)、nginx.conf、Dockerfile

QQ_1784207661493.png

2.5.3 docker-compose.yml ui 服务配置

在前面的docker-compose.yml中添加ui

version: '3'

services:
  ui:
    build:
      context: ./ui
    container_name: md-ui
    ports:
      - "8102:80"
    environment:
      - TZ=Asia/Shanghai 

    restart: unless-stopped
    volumes:
      - ./ui/nginx.conf:/etc/nginx/nginx.conf
      - ./ui/dist:/usr/share/nginx/html/dist
2.5.4 启动容器脚本
 ./autorun.sh

QQ_1784208169732.png

2.5.5 容器查看与报错说明

查看容器命令:docker ps

QQ_1784208475450.png

QQ_1784208458598.png

报错原因:nginx.conf 仅片段,缺少完整 http 外层配置。

2.5.6 完整可用 nginx.conf
worker_processes auto;

events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    sendfile on;
    keepalive_timeout 65;

    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    server {
        listen 80;
        server_name localhost;

        root /usr/share/nginx/html;
        index index.html;

        location / {
            try_files $uri $uri/ /index.html;
        }

        location /api/ {
            proxy_pass http://192.168.55.140:8101/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        location /md-docs/ {
            proxy_pass http://192.168.55.140:8101/md-docs/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

2.5.7 重新部署验证

执行重启命令:

docker-compose stop
docker-compose rm
./autorun.sh
docker ps

QQ_1784208593676.png 线上访问地址:[http://192.168.55.140:8102/]

QQ_1784208622395.png

QQ_1784208656027.png

登录、搜索、文档详情全部功能正常,前端容器部署完成。

三. 小结

3.1 接口代码聚合在一起

OpenCode 生成完整 Vue3 前端代码,包含登录、文档检索、详情渲染全套业务逻辑,但代码结构存在聚合问题,全部接口与请求拦截器写在同一文件:

import axios from 'axios'
import { getToken, clearAuth } from '../utils/auth'
import router from '../router'

const api = axios.create({
  baseURL: '',
  timeout: 15000
})

api.interceptors.request.use(config => {
  const token = getToken()
  if (token) {
    config.headers['Authorization'] = token
  }
  return config
})

api.interceptors.response.use(
  res => res.data,
  err => {
    if (err.response && err.response.status === 401) {
      clearAuth()
      router.push('/')
    }
    return Promise.reject(err)
  }
)

export function getSmsCode(mobile) {
  return api.post('/auth/user/getSmsCode', { mobile })
}

export function smsVerify(mobile, code, verify) {
  return api.post('/auth/user/smsVerify', { mobile, code, verify })
}

export function search(keywords, page = 1, pageSize = 10) {
  return api.get('/md/search', { params: { keywords, page, pageSize } })
}

export function getDocDetail(id) {
  return api.get('/md/detail', { params: { id } })
}

当然,这也不是什么大问题,各人写代码的习惯不同而已。我的开发习惯会将接口拆分目录、请求工具抽离至 utils,该规范不作强制要求。如果告诉OpenCode也是可以改过来的。

3.2 多代理问题

改造前多代理 vite 配置参考:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,
    proxy: {
      '/auth': {
        target: 'http://localhost:8101',
        changeOrigin: true
      },
      '/md': {
        target: 'http://localhost:8101',
        changeOrigin: true
      },
      '/md-docs': {
        target: 'http://localhost:8101',
        changeOrigin: true
      }
    }
  }
})

优化方案:统一 /api 前缀代理,减少多处配置维护成本。

3.3 OpenCode问题

OpenCode 生成代码存在较多逻辑缺陷,包括命名冲突、路由判空缺失、图片路径未适配、登录缓存逻辑不完善,需要具备前端基础的人员调试修复,这时要提高代码质量就需要有一个架构比较好的程序员来指导。

3.4 拓展使用场景

除 Web 前端外,已使用 OpenCode 生成 Android 原生、鸿蒙端代码,因本地环境未调试完成无相关截图记录。

3.5 落地经验

先统一内部基础开发框架,再使用 OpenCode 生成业务页面,兼顾开发效率与项目代码规范。