# Ciyon移动端开发框架 Skill指南
## 框架概述
CiYon是一个基于uniapp的轻量级跨平台移动应用开发框架,采用Vue3 + Vite技术栈,支持小程序、H5、App多端发布。
## 项目结构
```
ai_mapshare/
├── fapp/ciyon_ap/ # uniapp移动端项目
│ ├── pages/ # 页面目录
│ │ ├── main/ # 参考主页面
│ │ ├── me/ # 个人中心
│ │ ├── pub/ # 公共页面
│ │ └── demo/ # 示例页面
│ ├── components/ # 60+自定义组件库
│ ├── util/ # 工具函数库
│ ├── static/ # 静态资源
│ ├── App.vue # 应用入口
│ ├── main.js # 主入口文件
│ ├── pages.json # 页面配置
│ ├── manifest.json # 应用配置
│ └── vite.config.js # Vite配置
```
## 技术栈
- **前端**: Vue3 + Vite + Uniapp
- **通信**: HTTP POST API(JSON)
- **参考页面**:
- /fapp/ciyon_ap/pages/demo/curd/demo_list.vue
- /fapp/ciyon_ap/pages/demo/curd/demo_edit.vue
- /fapp/ciyon_ap/pages/main/index.vue
- /fapp/ciyon_ap/pages/main/me.vue
## 开发规范
### 页面开发规范
- 页面存放在 `pages/` 对应模块目录下
- 页面注册在 `pages.json` 中配置路由
- 使用uniapp标准生命周期
### 目录及文件名命名规范
- 在`pages/`下新建项目目录
- `/pages/[项目目录]/index.vue`作为首页主页面
- `/pages/[项目目录]/me.vue` 作为个人中心主页面
- `/pages/[项目目录]/xx_xx.vue` 其他页面均要有一定含义,建议与数据表名称相关。模块_功能
- 例如:`order_list.vue` 列表页、`order_info.vue` 详情页、`order_edit.vue` 编辑页、`order_ship.vue` 功能页
- 例如: `user_info.vue`、`user_order.vue`、`user_address.vue`
- 页面起名尽量贴合业务,不要用`detail.vue`、`list.vue`、`edit.vue`、`add.vue`等通用名称。
### vue文件API方法命名规范
- 页面初始化方法一般为`init`
- func: 'xx.xx_init'
- 列表页,初始化传入once(bool)参数,once=true,首次请求获取初始化数据
this.pagepost.once = !this.init.once;
参考 /fapp/ciyon_ap/pages/demo/curd/demo_list.vue
参考 /fapp/ciyon_ap/pages/demo/curd/demo_edit.vue
API参考 /web/ampap/demo/demo.php
- 更新 `update`、删除 `delete`、审核 `audit`等方法按此命名。
- 综合举例:
- 前端文件: /pages/[项目目录]/[模块]_list.vue
- 前端调用: this.callfunc({func:'[模块].[功能]_init'})、this.callfunc({func:'[模块].[功能]_delete'})
- 后端文件: /web/am[项目目录]/[模块].php
- 后端函数: json_[功能]_init()、json_[功能]_delete()
### 组件使用规范
组件前缀统一使用 `ciy-`,已提供60+组件:
**表单组件**:
- `ciy-input` - 基础输入框
- `ciy-textarea` - 文本域
- `ciy-select` - 选择器
- `ciy-radio` - 单选
- `ciy-checkbox` - 复选
- `ciy-checkitem` - 检查项
- `ciy-switch` - 开关
- `ciy-selbool` - 布尔选择
- `ciy-selcas` - 级联选择
- `ciy-selpage` - 页面选择
- `ciy-slider` - 滑块
- `ciy-inputnumber` - 数字输入
- `ciy-inputbet` - 范围输入
- `ciy-inputcyc` - 周期输入
- `ciy-inputdatetime` - 日期时间
- `ciy-inputdaterange` - 日期范围
- `ciy-inputtimepoint` - 时间点
- `ciy-inputunitedit` - 单位编辑
- `ciy-inputocr` - OCR输入
- `ciy-capcode` - 验证码输入
- `ciy-searchbar` - 搜索栏
- `ciy-query` - 查询组件
- `ciy-selmap` - 地图选择位置
- `ciy-mapbox` - 地图展示组件(基于 Leaflet)
- `ciy-upload` - 文件上传
**展示组件**:
- `ciy-showmoney` - 金额展示
- `ciy-shownum` - 数字展示
- `ciy-showimgs` - 多图片展示
- `ciy-textmore` - 文本展开
- `ciy-chart-pie` - 饼图
- `ciy-markdown` - Markdown渲染
- `ciy-svgimg` - 单图片显示
- `ciy-totalsem` - 总计语义
- `ciy-gesture` - 手势识别
- `ciy-handsign` - 手势识别
- `ciy-audio` - 音频播放
**功能组件**:
- `ciy-camera` - 相机(在ciy-aicameraocr中)
- `ciy-aivoice` - AI语音
- `ciy-gesture` - 手势识别
- `ciy-handsign` - 手势识别
- `ciy-btreader` - 蓝牙读卡器
- `ciy-header` - 页面头部
- `ciy-tabbar` - 底部导航
- `ciy-dialog` - 弹窗
- `ciy-toast` - 提示
- `ciy-alert` - 警告
- `ciy-popmenu` - 弹出菜单
- `ciy-calendar` - 日历
- `ciy-ratestar` - 评分
- `ciy-segment` - 分段器
- `ciy-swiper` - 轮播
- `ciy-swipelist` - 滑动列表
- `ciy-listend` - 列表底部
- `ciy-movable` - 可移动
**动画组件**:
- `ciy-ani` - 动画
- `ciy-aniheight` - 高度动画
- `ciy-anipop` - 弹出动画
**认证组件**:
- `ciy-auth` - 认证
**自定义组件**:
- `diy-apuser` - AP用户
- `diy-xxlist` - XX列表
**调试组件**:
- `ciy-dbg` - 调试
### API通信规范
**请求封装**:
```javascript
// 使用 callfunc 方法调用API
await this.callfunc({
func: 'main.index_init', // API函数名,格式:后端API文件名.前端页面名_方法
data: { // 要发送的数据
id: 123,
name: 'test'
},
srv: 't', // 服务器标识,默认't'
loadhide: false, // 是否隐藏加载提示,默认false
cache: 0, // 缓存时间(秒),0不缓存,一般不缓存
cachekey: '' // 自定义缓存键
}).then(res => {
// 处理响应
});
```
**API路径规则**:
- 前端调用格式:`{func: 'module.method'}`
- URL映射:通过 `jsnurl` 配置映射到实际URL
- API命名与页面关联:方法名应与前端页面文件名对应,便于识别和维护。格式建议:`{后端API文件名}.{前端页面名}_{操作方法名}`
- 示例:`{func: 'aaa.index_init'}` 对应后端API `/web/ambap/aaa.php`、对应移动端 `pages/aaa/index.vue`
- 示例:`{func: 'bbb.detail_init'}` 对应后端API `/web/ambap/bbb.php`、对应移动端 `pages/bbb/detail.vue`
- 示例:
- 前端调用:`{func: 'main.index_init'}`
- 另一个示例:`{func: 'login.login_mobile'}`
- **重要**:后端函数名必须与前端的 func 参数完全一致,例如前端 `func: 'aimap.index_init'`,后端函数名为 `json_index_init()`
**响应格式**:
```javascript
// 成功响应
{
code: 1, // 1表示成功
// 其他业务数据...
}
// 失败响应
{
code: 非1, // 非1表示失败,2表示需要重新登录
errmsg: '错误信息', // 错误提示
// 其他错误数据...
}
```
### 工具函数
**util/ciy.js** - 核心工具库:
**API请求**:
- `callfunc(opt)` - 统一API请求方法
- `calltxt(opt)` - 纯文本请求
**存储管理**:
- `getstorage(key, def)` - 读取storage
- `setstorage(key, val)` - 写入storage
- `removestorage(key)` - 删除storage(支持通配符*)
- `clearstorage()` - 清空所有storage
**用户认证**:
- `getauth()` - 获取用户信息,未登录自动调起登录
**提示交互**:
- `alert(content)` - 弹窗提示(await)
- `toast(content, icon)` - 轻提示(await)
- `askmsg(content)` - 询问确认框(await)
- `inputmsg(content, def)` - 输入框(await)
- `popmenu(options)` - 弹出上拉菜单(await)
**页面跳转**:
- `gourl(url, type, initdata, initkey)` - 页面跳转
- `http://` - 打开H5页面
- `!` - 打开原型图页面
- `*` - alert提示
- `$` - 需要登录
- `%` - 需要登录且完善信息
- `^` - 需要登录且实名认证
- `&` - 需要登录且绑定银行卡
- `goweb(url, name)` - 带授权打开H5页面
- `goloc(lat, lng, bet)` - 打开地图
- `gophone(phone)` - 拨打电话
**数据处理**:
- `json_parse(data)` - 字符串转json
- `json_string(data)` - json转字符串
- `toint(val)` - 转整数
- `tofloat(val)` - 转小数
- `tostr(val)` - 转字符串
- `tostamp(val)` - 转时间戳
- `todatetime(val)` - 时间戳转日期时间
- `tofix(val, dec)` - 处理小数显示
- `tounit(val)` - 显示多级单位
- `tocyc(val)` - 显示周期 月/天/分钟
- `tomsk(val)` - 显示文字遮罩
- `totimespan(val)` - 将时间转成 xx天后/xx小时前
- `totimesec(val)` - 将秒数转成 xx天/xx小时
- `totimepoint(val)` - 将数字转成 xx:xx[:xx]
- `todayage(val)` - 返回日龄天数
- `tobr(val)` - 将\n替换成
,用于显示多行文本。结合 v-html 使用:`v-html="tobr(item.descs)"`
- `topad0(num, len)` - 数字前面补0
- `tonumtho(num)` - 整数部分千分位显示
- `tonumdec(num)` - 小数部分
**类型校验**:
- `isfloat0(val)` - 校验float是否0
- `isarray(val)` - 判断是否array
- `isobject(val)` - 判断是否object
- `islocalmedia(val)` - 判断是否本地文件
- `isimg(val)` - 是否图片
- `isvideo(val)` - 是否视频
- `file_ext(filename)` - 获取文件扩展名
- `file_stor(url)` - db存储的url转云存储的绝对url
**字典处理**:
- `ccode(catalog, val, key, def)` - 以id查询字典中的值
- `scode(catalog, vals, key)` - 以ids查询字典中的多个name
- `mcode(catalog, val)` - 以id查询字典中的多级name
- `bcode(catalog, val)` - 以int查询字典中的多个name
- `hascode(val, codes)` - 某个数字是否在多个数字中
- `hasstr(str, substr)` - 字符串是否包含另一个字符串
**编码转换**:
- `enbase64(str)` - base64编码
- `svg2bg(svg)` - 将svg转成backgroundImage语法
**其他工具**:
- `sleep(ms)` - 延迟毫秒(await)
- `copyboard(text)` - 拷贝文本
- `getstrparam(str, sep)` - 解析简易参数
- `urlparam(url)` - 解析url
- `shareparam(opt)` - 分享链接转换
- `arrayfind(arr, val)` - 从数组中匹配值
- `objdeepmerge(dest, src)` - object合并
- `objtolist(list, obj/id)` - 将object并入list数组,将id从list数组中移除
- `objclone(obj)` - 深度复制obj
- `str2date(str)` - 字符串转Date
- `bin2hex(bin)` - bin转hex字符串
- `hex2bin(hex)` - hex字符串转bin
- `style2obj(style)` - 将style属性转成object
- `nopower(power)` - 权限检查
- `getroute()` - 获取当前路由
- `getpage(idx)` - 获取当前/上页页面
- `getrect(selector)` - 获取元素尺寸(await)
- `file_upload1(file)` - 上传单个文件返回url(await)
- `file_uploads(files)` - 上传多个文件(await)
- `go(e, cb)` - golang式异步错误处理
- `goe(e, cb)` - golang式异步错误处理(增强版)
**页面控制**:
- `pagenoscroll(isno)` - 禁止页面滚动
- `settheme()` - 设置暗黑模式
- `setfont()` - 设置敬老模式
- `setTabbar()` - 设置Tabbar
- `executepnt(pntid)` - 触发积分埋点
**加载资源**:
- `load_ciydict()` - 获取远程静态dict(await)
- `load_svgicon(url)` - 获取远程静态svgicon(await)
**扫描功能**:
- `scanqr()` - 扫二维码(await)
### 开发流程
**App.vue 全局配置**:
- app.globalData.jsnurl.t,配置入口域名的API入口 例如 'https://ciyon.local.ciy.cn/ambap/'
- 在development中 配置本地的入口域名的API入口(用于调试,一般以.local.ciy.cn为主体)
- app.globalData.tabbarArr,作为底部TabBar定义,全局定义给ciy-tabbar组件使用。
- tabbar虽然自定义,但pages.json需正常配置。且App.vue的globalData.mainpage应指向首页。
- tabbar图标用svg定义,默认双色模式。选中颜色bgsel1/bgsel2,未选中颜色bggray1/bggray2。
- **tabbarArr 配置要点**:
- `fullpath` 必须与实际页面路径一致,如 `/pages/aimap/index`
- `name` 使用 i18n 代码,需在 `util/lang/*.json` 中同步配置翻译
- 所有 tabbar 页面都必须在模板底部添加 `` 组件
- **语言翻译配置**:所有 tabbar 的 name(如 tabbar.topic、tabbar.msg)需在所有语言文件(zh-Hans.json、en.json、zh-Hant.json、ja.json、fr.json)中添加对应的翻译
**新建页面**:
1. 在 `pages/` 对应模块下创建 `.vue` 文件
2. 在 `pages.json` 中注册页面路由
3. 使用组件和工具函数开发功能
**新建组件**:
1. 在 `components/` 下创建组件目录和文件
2. 组件名使用 `ciy-` 前缀
3. 支持通过 props 传递参数
4. 通过 $emit 触发事件
5. 有用户的明确指令,才能新建组件
**调用API**:
1. 确定API函数名,格式:`模块.方法`,如 `main.index_init`
2. 使用 `callfunc()` 发送请求:
```javascript
var retjson = await this.callfunc({
func: 'main.index_init',
data: {
id: 123
}
});
if (retjson.code != 1)
return this.alert(retjson.errmsg);
// 处理成功响应
```
3. 处理返回数据,成功时 `code=1`,失败时有 `errmsg`
**首次请求优化**:
页面首次加载通过 `once` 标志位一次性返回所有初始化数据,减少后续翻页重复带出初始化数据:
```javascript
async getlist() {
this.pagepost.pageno = this.pageno + 1;
this.pagepost.once = !this.init.once;
var retjson = await this.callfunc({
func: 'demo.index_list',
data: this.pagepost
});
this.pageno++;
if (this.pageno == 1)
this.init.list = [];
this.init = this.objdeepmerge(this.init, retjson);
if (retjson.once) {
}
}
```
### 样式规范
- 全局样式: `util/style.css`
- 组件内使用 scoped CSS
- 使用 rem 单位适配
### 常用组件
**表单组件**:ciy-input、ciy-select、ciy-radio、ciy-checkbox、ciy-selbool、ciy-inputbet、ciy-upload等
**展示组件**:ciy-showmoney、ciy-showimgs、ciy-markdown等
**功能组件**:ciy-auth、ciy-dialog、ciy-toast、ciy-popmenu等
**导航组件**:
- ciy-header - 页面头部(所有页面使用,navigationStyle为custom)
- ciy-tabbar - 底部导航(tabbar页面必须添加)
- ciy-segment - 分段器(使用 `:lis` 属性传递数组,如 `:lis="[{id:1,name:'选项1'}]"`,不要使用 `:options`)
**地图组件**:
- ciy-selmap - 国内地图选择组件(微信小程序用)
- ciy-mapbox - 通用地图展示选择组件(基于 Leaflet)
- 经纬度在数据库中存储时,应乘以 10000000,取整数
- 例如:30.2589121 → 存储为 302589121
- 在前端使用时,除以 10000000 恢复原始值
**ciy-mapbox组件地图瓦片提供商**:
- 推荐使用 Esri World Imagery(卫星影像)+ CartoDB Positron Labels(标注层)
- Esri World Imagery 提供高质量的卫星影像,适合展示地理位置
- CartoDB 标注层提供清晰的文字标注,可叠加在影像层上方
**复杂查询组件**:
- ciy-query - 支持多种输入类型(input、select、checkbox、radio、date、range等)
### 常用配置
**pages.json** - 页面配置:
- 页面路由
- 导航栏样式
- 底部TabBar(仅需配置 `pagePath`,其他配置由 App.vue 的 `tabbarArr` 定义)
- 全局样式
- 分包配置:主包放置核心页面(如 aimap、pub),其他功能页面放入子包以优化性能
**manifest.json** - 应用配置:
**manifest.json** - 应用配置:
- 应用信息
- 权限配置
- SDK配置
- 多端打包设置
**vite.config.js** - 构建配置:
- 别名配置
- 插件配置
- 构建优化
- 所有页面编译时都带ciy-auth、ciy-alert、ciy-toast、ciy-popmenu组件,无需手写
## 框架已完成事项(无需开发)
### 注册登录、找回密码功能与后端login.*接口
复用,基本无需修改ciy-auth组件和login API接口
新建,需复制ap_user、ap_usr_*有关表结构。
登录成功,自动同时加载所有字典项到前端,前端缓存到localstorage备用。
### 默认navigationStyle为custom
使用ciy-header组件定义顶部标题栏。可复制新组件自定义。
使用ciy-tabbar组件定义底部导航栏。可复制新组建自定义样式。已自动隐藏系统自带的默认tabbar。
### 已完成通用组件
任意页面均支持未登录 弹出登录/注册界面。
弹出框已美化,支持ciy-alert、ciy-toast
弹出菜单已美化,ciy-popmenu组件,支持多种排版方式,突破了默认组件的数量限制。
### 已给所有page注入通用变量
this.g,为所有缓存的字典项
this.me,为登录的用户完整数据
this.init,作为页面初始化,统一接收后端请求返回的数据。一般用this.init.code!=1作为骨架屏标识。
this.opn,页面传入参数可随意使用,不用在onload另行存储。
this.pageno,翻页页码
this.pagedata,页面间传递数据,对于页面深度点击传参非常有效。可替代vuex/Pinia
this.pagepost,页面object变量
## 注意事项
1. **命名规范**: 组件统一使用 `ciy-` 前缀目录
2. **API调用**: 使用 `callfunc()` 方法,函数名格式为 `模块.方法`,后端函数名必须与前端 func 参数对应
3. **Token管理**: 自动处理Token,无需手动管理,Token存储在 `localStorage` 中
4. **响应格式**: 成功 `{code: 1, ...}`,失败 `{code: 非1, errmsg: '错误信息', ...}`
5. **组件复用**: 优先使用现有组件,避免重复开发
6. **多端适配**: 使用uniapp条件编译处理平台差异
7. **性能优化**:
- 使用 `cache` 参数设置缓存时间
- 避免频繁请求
- 使用 `loadhide: true` 取消不必要的加载提示
- 合理使用分包,主包放置核心页面,其他功能放入子包
8. **页面跳转**: 使用 `gourl()` 方法,支持多种特殊前缀($需要登录、*提示等)
9. **数据传递**: 使用 `pagedata` 对象进行页面间数据传递
10. **字典数据**: 通过 `g` 对象访问字典数据,自动同步更新
11. **用户信息**: 通过 `me` 对象访问当前用户信息
12. **路由参数**: 通过 `opn` 对象访问页面URL参数
13. **用户认证**: 注册、登录、找回密码等功能已集成在 `ciy-auth` 组件中,直接复用,无需重复开发
14. **TabBar配置**:
- pages.json 中只配置 `pagePath`,其他样式由 App.vue 的 `tabbarArr` 定义
- tabbarArr 中的 `fullpath` 必须与实际页面路径完全一致
- 所有 tabbar 页面必须在模板底部添加 ``
- tabbarArr 中的 `name` 需在所有语言文件中配置翻译
15. **组件使用注意**:
- ciy-segment 使用 `:lis` 属性传递数组,不要使用 `:options`
- 遵循框架定义的属性名称,避免使用非标准属性
## 编译及自动调试
运行cmd,编译移动端H5
"D:\Program Files\HBuilderX\cli.exe" publish --platform h5 --project "D:\Dreams\ciy\ai_xxx\fapp\ciyon_ap" --webTitle 众产
编译后访问: https://xxx.local.ciy.cn/ 调试