恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Vue3+Pinia 电商后台实战:购物车、订单到支付闭环完整实现

  • 首页
  • 资讯中心
  • /
  • Vue3+Pinia 电商后台实战:购物车、订单到支付闭环完整实现

相关资讯

手写响应式状态内核:从依赖收集到 React 适配的完整实践 2026/10/18 9:06:45
Unity Shader实现摆钟双图切换:UV角度计算与边缘柔化 2026/10/18 9:06:45
Unity客户端连接服务端失败的三大核心原因与跨平台解决方案 2026/10/18 9:06:45

最新资讯

智能世界2030:以时间成本重构技术价值的底层逻辑
基于Python的3D-CT肺结节检测:从数据预处理到模型调优的实战指南
跳槽投递要不要海投?用数据思维做投递策略与简历优化
DeepSeek Harness排错实战:npx卡顿、端口占用、插件清单损坏全解决
MFC Socket一对多即时通讯实战:多线程阻塞服务器与客户端框架解析
A股多维度量化择时系统构建与回测避坑指南

今日推荐

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本周热门

基于GMM的语音识别系统原理与MATLAB实现详解
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Vue3+Pinia 电商后台实战:购物车、订单到支付闭环完整实现

发布时间:2026/10/18 9:06:45
Vue3+Pinia 电商后台实战:购物车、订单到支付闭环完整实现 做电商类管理后台最容易被低估的是那条“交易闭环”。商品列表做得再花哨一旦走到购物车、订单、支付这条链路上状态管理、路由权限、接口时序全都会堆到一起稍不留神就是一堆隐性 bug。这次分享的项目我习惯叫它“甄选商城二”是上一期商城项目的续作技术栈继续沿用 Vue3 TypeScript Pinia Vue Router 4 Vite目标很明确把从加购到支付成功的完整流程做扎实。很多同学写商城项目时习惯停留在展示层——列表、详情、登录都能做但一旦切换到真实业务逻辑——多角色菜单、共享购物车、订单金额校验、支付状态查询——就不知道怎么组织代码了。这个项目就是用来补上这些实战断层的你拿到的不是一堆孤立页面而是一套能从登录一路跑到支付完成的状态流转方案。适合已经学过 Vue3 基础、想往工程化和复杂业务方向走的开发者如果路由和 Pinia 的基本使用还不熟建议先把上一篇的登录和商品模块过一遍再来看这篇。文章基本按我的开发顺序讲先说明为什么这样拆技术栈和目录再把权限、购物车、结算、支付四个难点逐个拆开接着给可直接抄的代码和调参思路最后单独整理一份实际调试中踩坑的排查手册。里面有完整代码也有不少文档里不会写的注意事项看完可以直接在自己的项目里复用。1. 先理清思路项目整体设计与技术选型1.1 技术栈选型背后的三个理由先说选型。项目延续 Vue3 和 TypeScript 组合不是跟风核心原因是这套电商流程里跨页面共享的状态太多了。用户信息、购物车、订单快照、角色权限任何一个模块单独维护都会导致状态在执行链路上断层。Vue3 组合式 API 的好处是把同一业务域的变量、函数、计算属性聚合在一起比如购物车逻辑可以整体做成一个 store 函数块而不是像 Vue2 那样在 data、methods、computed 三个选项之间来回找。开发者维护时一个页面需要哪些状态直接看 useCartStore 返回了什么就行心智负担小很多。TypeScript 的价值通常被低估。购物车里的商品字段、订单对象、接口返回值如果不做类型约束项目跑到中后期最怕的就是字段被悄悄改掉。比如商品价格从 number 被改成 string页面计算金额时就会全变 NaN。有了 interface 约束后接口层和后端字段对上组件对不上的时候编辑器第一时间就会报错。项目里我会给 OrderItem、CartItem、UserInfo 单独建模型文件这也是工程化项目的基本习惯。再说 Pinia。替换 Vuex 不是因为性能差异而是心智负担真的不一样。Vuex 要写 state、mutations、actions 一整套路子同步修改状态还要绕个弯Pinia 里直接 store ref function类型推导也好很多。尤其做购物车这种高频读写的状态Pinia 的 setup store 形式让代码简洁得多。Vite 就不用多说了冷启动和处理 TS 类型速度都明显快于传统 dev server对调试长链路流程帮助很大。1.2 这个项目相比上一版的定位差异上一版如果说是“展示型商城”这一版就是“交易型商城”。展示型核心在做商品信息的呈现——登录、列表、详情、搜索、个人信息交易型则聚焦钱和货的流转——购物车数据怎么跨页面共享、订单确认前要做哪些校验、支付状态怎么同步到前端。这是两个完全不同的复杂度层级。模块上一版侧重点这一版侧重点商品列表渲染、筛选排序库存联动、下架状态处理购物车本地数组演示全局共享、持久化、选中计算订单暂无或静态页面地址、明细、金额、提交幂等权限登录后跳转动态路由 按钮级控制表格里每一项都对应一类真实业务问题。比如库存联动如果不限制购物车数量超过库存下单后后端再报错体验就会断裂。这一版要求把能前置的校验尽量放在前端做掉同时不能只做前端后端 mock 接口也要有对应校验落地的是“双重校验”思路。这种前后端都考虑问题的方式才是真实项目里需要的。1.3 目录结构提前划分好模块边界项目目录我习惯从第一天就分清楚避免写到后面变成一座无组织的大楼。src ├─ api # 接口层按模块拆分 │ ├─ product.ts │ ├─ cart.ts │ ├─ order.ts │ └─ user.ts ├─ router # 路由表 静态路由 权限逻辑 ├─ stores # Pinia 状态 │ ├─ user.ts │ ├─ cart.ts │ └─ order.ts ├─ layout # 后台框架侧边菜单、头部 ├─ views # 页面组件 │ ├─ home │ ├─ cart │ ├─ checkout │ ├─ payment │ └─ user ├─ directives # 自定义指令v-permission ├─ utils # request.ts、auth.ts、format.ts ├─ mock # mock 接口与模拟数据 └─ types # 全局类型定义划分边界总结成一句话api 层只做请求stores 只做状态和动作页面只做视图编排。很多项目越写越乱就是因为请求直接写在组件里同类逻辑散落十几个文件后期想统一加鉴权头、统一处理 401 都无比痛苦。这个目录看起来多实际每个目录职责单一后面所有模块往里填就行。2. 拆开难点权限、购物车、结算与支付怎么落地进入核心细节之前先说一句这一章的四个难点是电商类项目真正的分水岭。能写出页面效果的人很多能把权限和状态流转写明白的人不多。下面逐个拆。2.1 动态路由与按钮权限权限不是只藏入口很多初学者的权限方案是“登录后根据角色用 v-if 把菜单藏掉”。真实验收时这种方案一测就穿菜单虽然不显示但路由表里仍然注册着所有页面懂一点开发的人直接改 URL 就能访问未授权页面。服务端不兜底的话后台相当于裸奔。动态路由的意义就在于此路由表本身就是按角色生成的没有权限的路由压根不会注册到当前应用中。实现思路分三步。第一步登录成功时后端返回当前用户的路由标识列表比如 admin 有全部页面editor 只有部分页面。第二步前端拿到标识后从“完整路由映射表”里筛出对应路由对象通过 router.addRoute 动态注册。第三步菜单组件根据当前路由表渲染。这里有个关键点addRoute 添加的路由刷新后如果没重新生成页面就会 404后面排查章节会专门讲。按钮级权限用的方案是自定义指令 v-permission。指令里读取当前用户的权限码比如订单按钮需要 order:create没有权限就直接移除元素。注意指令的执行时机如果权限码是异步拉取的指令首次执行时可能还没拿到权限码要在 updated 钩子里再检查一遍否则数据加载完按钮会被重新渲染出来。这个细节特别容易踩坑。另一个高频问题是两个角色来回切换时之前动态注册的路由还留着。切到低权限账号后高权限路由依然能访问。解决方式是在切换角色前把之前 addRoute 的路由用 router.removeRoute 清理干净。很多项目没做这步就会出现明显的越权漏洞。2.2 购物车全局状态Pinia 设计与持久化细节购物车为什么必须放进全局 Store因为同一个商品会在详情页加购、在购物车页调整数量、在结算页被读取还要在顶栏显示角标数量。如果每个页面各自存一份数据同步全靠事件总线流程一长就会乱套。用 Pinia 后所有页面通过同一个 useCartStore 实例读写状态天然一致。设计上我分了四层cartList 管数据、totalCount 和 totalPrice 管派生统计、action 管变更、persist 管持久化。特别注意 price 字段我用“分”而不是“元”存储。先说结论浮点数在 JavaScript 里做加法会出现 0.1 0.2 等于 0.30000000000000004 的情况购物车金额又是高频累加最容易踩雷。用分做整数运算最后格式化回元既精确又好维护。提示价格单位用分是购物车金额计算的基础约定也是后面订单模块能稳定计算的前提。持久化我用的是最简单的 localStorage 手动方案每次 action 变更后调用 persist 写入。没有用三方持久化库是因为购物车数据结构简单、变更点可控手写几十行反而更透明。读取时用 JSON.parse 再套一层 try/catch避免脏数据直接让整个页面崩掉。登录后再把本地结算数据合并到用户购物车也是在 store 里做action 命名为 mergeCart。这样用户没登录时可以先用本地购物车登录后自动合并体验更接近真实商城。2.3 结算页最容易翻车的五个校验点结算页不是简单把商品列表再做一遍。它承担的是订单入口每个字段都直接影响后端数据。我总结出五个高频校验点地址为空、库存不足、金额为零、重复提交、商品已下架。地址为空默认地址不存在时先让用户进入地址管理而不是直接允许提交。库存不足订单里的数量要再次和后端返回的库存做对比不能只信购物车里的缓存值。金额为零运保费、优惠券叠加后出现零元订单要直接拦截防止测试数据污染订单记录。重复提交提交按钮加 loading 状态 请求中标志位防止连点两次产生两笔订单。商品下架提交前调接口校验商品状态接口返回失效时给出清单并回退到购物车。前端校验做完只是第一道门。后端 mock 接口里同样要校验这五项哪怕只是模拟数据也要这样写。原因很现实如果项目里 mock 接口没有对应逻辑面试时一问“后端怎么兜底”就会露馅。把 mock 接口也按真实约束写一套才能真正理解接口参数为什么要设计成那样也方便以后接真实后端时无缝切换。2.4 模拟支付轮询的正确打开方式真实支付流程是前端跳转支付平台支付平台回调后端后端再通知前端。但本地 mock 服务端没有推送能力拿不到支付平台回调所以前端只能用轮询模拟这个“通知”过程用户扫描二维码完成支付后前端每隔几秒去查一次订单状态查到已支付就跳转结果页。轮询最忌讳一进来就乱开 setInterval。要先按订单号查询一次初始状态如果已经是已支付就直接跳转不要重复轮询。然后设置 interval通常 3 秒一次比较合理太频繁接口压力大太久用户等得着急。组件卸载时必须 clearInterval否则用户切走页面后定时器还在跑轻则多打无用请求重则内存泄漏。另一个容易被忽略的点是防重复轮询。如果订单详情页和支付页各写了一个轮询一次支付后两个页面同时轮询到已支付状态就可能同时触发跳转路由栈会被顶得乱七八糟。更合理的做法是把轮询逻辑抽成统一的 usePaymentPolling 组合式函数所有页面共用一套查询和清理逻辑。后面 3.4 会给完整代码。3. 直接上手从初始化到核心模块的实现代码前面把思路和难点讲清楚了这一章直接给可复现的实操过程。我从零搭一个小工程把权限、购物车、订单三段核心代码写出来。这里用 npm 做演示你用 pnpm 或 yarn 也都一样。3.1 环境搭建一条命令拉起 Vue3 TS 工程npm create vitelatest demo-shop -- --template vue-ts cd demo-shop npm install npm install pinia vue-router4 axios element-plus npm install -D vite-plugin-mock mockjs types/mockjs sass项目名 demo-shop 可以随便改不要带中文。接着配置 vite 别名在 vite.config.ts 里加 resolve.alias把 指向 src。这一步不是可选项后续所有 import 路径都依赖它不然写相对路径会越来越难维护模块增多后尤其明显。import { fileURLToPath, URL } from node:url export default defineConfig({ plugins: [vue(), viteMockServer({ mockPath: ./src/mock })], resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } } })Mock 用的是 vite-plugin-mockmockPath 指向 src/mock 目录它会自动读取目录下所有 ts 文件并注册为接口。这个插件只对开发环境生效正式构建时需要隔离相关代码否则上线后请求会 404。线上一般由真实后端承接接口这里的 mock 只服务于本地联调和演示。3.2 权限控制三步走登录、守卫、动态菜单登录模块的核心是拿到 token 和角色菜单数据。登录成功后的动作很简单token 持久化到 localStorage角色标识和菜单列表存进 Pinia后续所有权限判断都从这里读。// stores/user.ts export const useUserStore defineStore(user, () { const token refstring() const roles refstring[]([]) const menus refAppMenuItem[]([]) async function login(username: string, password: string) { const res await apiLogin(username, password) token.value res.token roles.value res.roles menus.value res.menus setToken(res.token) } async function fetchUserInfo() { const res await apiUserInfo() roles.value res.roles menus.value res.menus } function logout() { token.value roles.value [] menus.value [] removeToken() } return { token, roles, menus, login, fetchUserInfo, logout } })登录后 token 要同步持久化因为刷新页面时内存数据会清空Store 重新初始化后如果没有 token用户立刻被弹回登录页。角色标识和菜单列表也一并放进 Store后续动态路由、菜单渲染、按钮权限都直接读这份数据。fetchUserInfo 是为了刷新页面后重新拉取用户信息保证动态路由能重建。接下来是路由守卫这是权限控制里最容易写错的部分。// router/permission.ts router.beforeEach(async (to) { const userStore useUserStore() if (!userStore.token) { return /login?redirect${encodeURIComponent(to.fullPath)} } if (!userStore.menus.length) { await userStore.fetchUserInfo() const routes mapMenusToRoutes(userStore.menus) routes.forEach(route router.addRoute(route)) return { ...to, replace: true } } return true })关键点在 addRoute 之后 return { ...to, replace: true }。因为 beforeEach 执行时当前路由还没确认动态路由只在这一轮拦截后才注册完成如果不重新触发一次导航页面会停在白屏或 404。这个写法比 location.reload 或 next({ ...to, replace: true }) 更直观也是很多人最常漏的一步。菜单组件不用额外写复杂逻辑直接把当前路由表里带 meta.title 和 meta.icon 的记录渲染成侧边栏即可。角色切换时先 removeRoute 清掉旧角色注册的路由再按新角色 addRoute就不会出现权限串台。3.3 购物车 Store 核心代码逐段拆解购物车 Store 的核心写法如下用 setup store 风格代码按数据、计算、动作、持久化四块组织。// stores/cart.ts import { defineStore } from pinia import { ref, computed } from vue export interface CartItem { id: string name: string price: number // 单位分 count: number checked: boolean stock: number } export const useCartStore defineStore(cart, () { const cartList refCartItem[](loadCart()) const totalCount computed(() cartList.value .filter(item item.checked) .reduce((sum, item) sum item.count, 0) ) const totalPrice computed(() cartList.value .filter(item item.checked) .reduce((sum, item) sum item.price * item.count, 0) ) function addToCart(sku: OmitCartItem, count | checked) { const target cartList.value.find(item item.id sku.id) if (target) { target.count 1 } else { cartList.value.push({ ...sku, count: 1, checked: true }) } persist() } function toggleCheck(id: string) { const item cartList.value.find(item item.id id) if (item) item.checked !item.checked persist() } function changeCount(id: string, delta: number) { const item cartList.value.find(item item.id id) if (!item) return item.count Math.min(item.stock, Math.max(1, item.count delta)) persist() } function removeChecked() { cartList.value cartList.value.filter(item !item.checked) persist() } function persist() { localStorage.setItem(cart, JSON.stringify(cartList.value)) } function loadCart() { try { return JSON.parse(localStorage.getItem(cart) || []) } catch { return [] } } return { cartList, totalCount, totalPrice, addToCart, toggleCheck, changeCount, removeChecked } })代码里有三个点值得强调。第一价格单位是分注释里也标了避免后面的人误传成元。第二changeCount 用 Math.min 和 Math.max 把数量钳制在 1 到 stock 之间防止用户通过输入框把数量改成负数或超出库存。第三loadCart 里用 try/catch 包了一层localStorage 里的数据有可能被手动改坏或半路丢失解析失败时给一个空数组而不是让整个页面崩溃。totalCount 和 totalPrice 用 computed 派生组件里直接当普通值读取数据会自动更新。有的写法会用 watch 监听 cartList 再持久化我这里选择每次 action 后显式调用 persist因为购物车操作入口可控。如果项目里很多组件会直接改 state那就用 watch 的 deep 模式兜底。两种思路都可以核心原则是持久化逻辑不能散落在组件里。3.4 订单提交流程从确认页到支付页的完整跳转订单提交链路是结算页点击提交、创建订单接口、拿到订单号、跳转支付页、轮询支付状态、成功跳转结果页。链路里任何一环都可能因为网络或数据问题中断所以接口返回和异常处理要写清楚。async function submitOrder() { if (submitting.value) return const cartStore useCartStore() const selected cartStore.cartList.filter(item item.checked) if (!selected.length) { ElMessage.warning(请先选择要结算的商品) return } submitting.value true try { const res await createOrder({ addressId: selectedAddress.value.id, skuList: selected.map(item ({ skuId: item.id, count: item.count })) }) orderId.value res.orderId cartStore.removeChecked() router.push(/payment/${orderId.value}) } finally { submitting.value false } }注意几个细节。submitOrder 里第一个 if 就是防重复提交配合按钮 disabled 形成双保险地址 ID 和商品列表在提交前要在页面里做二次校验创建订单成功后立刻清掉已结算商品这是购物车和订单状态同步的关键一步。如果这里忘记清用户可能拿着同一批商品再下第二单造成重复下单。支付页的轮询我抽成了一个组合式函数export function usePaymentPolling(orderId: string, onPaid: () void) { let timer: number | undefined async function checkStatus() { const res await getOrderStatus(orderId) if (res.payStatus PAID) { clear() onPaid() } } function start() { checkStatus() timer window.setInterval(checkStatus, 3000) } function clear() { if (timer) { window.clearInterval(timer) timer undefined } } onBeforeUnmount(clear) return { start, clear } }这个组合式函数把定时器生命周期和组件解耦组件卸载时自动清理定时器。注意 onBeforeUnmount 只能在组件 setup 上下文里注册如果应用场景不是组合式调用就要把 clear 暴露给页面在 onUnmounted 里手动调用。统一轮询入口后支付页和订单详情页就不会出现两个定时器同时查询的问题。3.5 构建优化打包体积与首屏加载的小改动项目功能完整后还要看一眼打包产物。Vue3 Element Plus 全量引入时主包很容易超过 1MB首屏加载明显变慢用户体验和项目观感都不好。第一步优化是路由懒加载让每个模块的代码只在需要时加载。写法很简单const Cart () import(/views/cart/index.vue) const Checkout () import(/views/checkout/index.vue)第二步是手动分包把核心框架和 UI 库单独拆成 chunk利用浏览器缓存减少重复下载。build: { rollupOptions: { output: { manualChunks: { vue-vendor: [vue, vue-router, pinia], element: [element-plus] } } } }分包不是分得越细越好每个 chunk 都有额外请求开销。把 Vue 相关框架打进一个包、Element Plus 单独一个包通常就够了。写这个项目时我还会重点检查 mockjs 有没有被打进产物。如果 vite-plugin-mock 没有按环境禁用构建时会带着模拟数据进入线上包体积变大不说还可能干扰真实接口。用 import.meta.env.DEV 判断只在开发环境启用 mock最稳妥。4. 避坑指南高频问题与排查技巧实录这一章是全文最实用的部分。每个人写的代码不一定一样但下面这些问题大概率会遇到至少会遇到其中几个。我按“现象、原因、排查”的方式整理后面再单独讲四个典型场景的定位思路。4.1 高频问题排查速查表现象常见原因排查顺序刷新后跳回登录页token 未持久化或读取时机不对看 localStorage 有没有 token看 Store 初始化有没有 getToken刷新后 404动态路由未重新生成在守卫里打印 addRoute 后的路由表确认是否执行购物车总价显示 0.30000000000000004金额用浮点数直接累加把价格改成分或金额格式化时取整轮询接口一直打不停定时器没有 clear找到 setInterval 对应的 clearInterval 调用链路切换角色后还能访问旧权限页面addRoute 的路由没有 removeRoute角色切换前清空动态路由表按钮点击两次产生两笔订单缺少提交中标志位加请求锁 按钮 loading支付页返回订单列表再进支付页轮询重复执行多个组件都启动了轮询统一轮询入口组件卸载时清理表格里每一项我都在调试时遇到过。前三个是入门必踩后几个是业务复杂度上来之后才暴露的。下面挑四个典型场景把定位思路说透。4.2 刷新就 404动态路由丢失问题复盘动态路由最常见的现象登录后一切正常一刷新就 404。原因很直接刷新时 Pinia 里存的 menus 被清空页面加载时没有动态路由可注册自然找不到对应页面。定位步骤分三步走先看控制台路由守卫日志确认刷新后是否调用了 fetchUserInfo如果调用后接口返回了菜单再看 mapMenusToRoutes 是否返回了空数组最后确认 addRoute 之后有没有重新触发导航。三步下来基本能锁定问题在哪一段。修复的关键是记住动态路由必须在应用初始化之后、实际页面访问之前注册。所以守卫里 addRoute 不能省fetchUserInfo 也不能放到某个页面组件内部去调用不然其他页面先被访问时动态路由还没生成。这个时序问题理解了后面遇到类似白屏问题就都能找到方向。4.3 0.30000000000000004金额计算的正确处理姿势这是电商项目里最经典的问题。直接给结论金额不要用元做浮点数统一用分。前端拿到后端返回的元时先乘 100 转成整数所有计算用整数最后展示时再除以 100 并格式化。工具函数两行搞定export function yuanToCent(amount: number): number { return Math.round(amount * 100) } export function centToYuan(cent: number): string { return (cent / 100).toFixed(2) }注意yuanToCent 里用 Math.round 而不是直接乘 100。因为 0.1 乘 100 在某些环境下会得到 10.000000000000002round 能把这个误差兜住。也别用 toFixed 做累加toFixed 返回字符串还带四舍五入累加次数一多偏差会越来越大。4.4 轮询导致的页面卡顿与泄漏轮询本身不会让页面变卡卡顿一般来自两个地方一是轮询接口返回的数据量越来越大前端又每次都全量更新响应式数据二是用了递归 setTimeout 但忘了清理定时器越积越多。我的建议所有轮询都放在组合式函数里timer id 用局部变量保存组件卸载钩子里 clear。轮询回调里只更新必要的响应式字段别把整个列表重新赋值。请求失败时不要无限重试连续失败三次就停下来给用户一个提示避免接口异常时客户端还在疯狂发请求。4.5 定位思路比修 bug 更重要最后补一句经验。遇到复杂问题不要瞎猜按“入口请求 → Store 状态 → 界面渲染”的顺序排查。页面卡住先看 Network 面板有没有连续请求角标不同步先看 Store 里 cartList 到底更新了没有权限异常先确认当前账号 token 和角色数据有没有变化。这个习惯一旦养成排查速度会快很多而且不容易漏掉问题源头。聊到这儿项目二的实战重点基本都覆盖了。说实话这类电商项目的难点不在技术有多新而在状态和时间线。把订单状态流转想清楚、把动态路由的注册时机搞明白远比自己堆几个动画效果有意义。最后分享一个小经验本地开发时我会准备两个角色账号一个管全部权限一个只读权限每次改完权限相关代码就轮流登录一次能快速暴露路由残留和按钮显示异常。你按这个思路把链路走通再遇到任何商城类需求基本都不会慌。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号