# Ciyon PC前端开发指南
## 框架概述
Ciyon是一个轻量级、高性能的PC前端开发框架,采用原生JavaScript开发,不依赖第三方库(如Vue、React、jQuery),专注于企业级后台管理系统和SaaS应用的快速开发。
### 核心特点
- **零依赖**: 纯原生JavaScript,无第三方库依赖
- **组件化**: 提供丰富的表单组件和业务组件
- **高性能**: 优化的DOM操作和事件处理
- **响应式**: 支持多端适配(PC、平板、手机)
- **国际化**: 内置多语言支持
- **主题系统**: CSS变量实现主题切换
---
## 框架架构
### 核心库文件
```
web/jscss/
├── ciy.js # 基础工具库和DOM操作
├── ciycmp.js # 表单组件库
├── ciycmp2.js # 扩展组件库
├── ciytable.js # 表格和列表组件
├── ciybigscreen.js # 数据大屏组件
├── ciy_websocket.js # WebSocket通信
├── style.css # 核心样式
└── theme.js # 暗黑模式
```
### 设计模式
#### DOM封装模式
框架使用 `$5()` 函数替代jQuery,提供统一的DOM操作接口:
```javascript
// 选择元素
var dom = $5('.class-name');
// 链式调用
dom.css({color: 'red'}).addClass('active').show();
// 事件绑定
dom.on('click', function(e) {
console.log('clicked');
});
```
#### 组件化模式
使用自定义标签和 `ciycmp()` 函数初始化组件:
```html
```
#### 面向对象模式
使用类封装复杂功能:
```javascript
var table = new ciyclass.table({
dom: '.table',
url: 'api/list',
pagecount: 20,
fn_done: function(json) {
console.log('data loaded');
}
});
table.callpage(1);
```
---
## 组件体系
### 表单组件
#### 日期时间选择器(ciy-datetime)
```html
```
**属性说明**:
- `com`: 组件名称(必填)
- `type`: 类型(date/datetime/month)
- `value`: 初始值(时间戳或日期字符串)
- `mindate`: 最小日期
- `maxdate`: 最大日期
- `placeholder`: 占位文本
#### 日期范围选择器(ciy-daterange)
```html
```
**输出格式**:`开始日期~结束日期`
#### 下拉选择框(ciy-select)
```html
```
#### 多选下拉框(ciy-selmulti)
```html
```
**输出格式**:`,id1,id2,id3,`
#### 级联选择框(ciy-selcas)
```html
```
用于省市区级联选择。
#### 开关(ciy-switch)
```html
```
**输出值**:`1`(开启)/ `2`(关闭)
#### 单位编辑器(ciy-inputunitedit)
```html
```
用于三级单位换算,如:1箱=20盒,1盒=24瓶。
#### 地图选择器(ciy-map)
```html
```
输出经纬度:`lng,lat`
#### 文本编辑器(ciy-textarea)
```html
```
支持Tab键、字数统计、@用户提示。
#### 文件上传(ciy-upload)
```html
```
**属性说明**:
- `type`: 允许的文件类型
- `maxcount`: 最大上传数量
- `maxkb`: 文件大小限制(KB)
### 列表组件
#### 表格组件(ciyclass.table)
```html
```
**功能特性**:
- 动态列配置
- 列宽调整记忆
- 列排序
- 列隐藏
- 行选择
- 分页
- 搜索
- 顶部选项卡筛选
**顶部选项卡筛选**:使用 `fillsearch` 的 `lidata` 参数添加顶部选项卡,`liall` 设置"全部"选项的文本:
```javascript
table = new ciyclass.table({
dom: '.table',
url: 'list',
pagecount: 20,
fn_beforedata: function(json) {
ciyfn.fillsearch({
searchdom: '.search',
data: json,
liall: '全部',
lidata: [
{id: 1, name: '未使用'},
{id: 2, name: '已使用'}
],
//lidata: '【字典代码】', // 引用字典写法
//lidata: ':全部.1:未使用.2:已使用', //数组简写
liclick: function(dom) {
table.search(dom, 'li');
}
});
return json;
}
});
```
**liid编号**:自定义标识值应从1开始,不要使用0。
**后端处理**:在 `setwhere` 函数中根据 `liid` 参数进行筛选。
#### 卡片列表(ciyclass.cardtable)
```html
```
### 功能组件
#### 弹窗(ciyfn.alert)
```javascript
ciyfn.alert({
title: '提示',
content: '操作成功',
btn: ['确定', '取消'],
cb: function(opn) {
if (opn.btn == '确定') {
// 确定操作
}
}
});
```
#### Toast提示(ciyfn.toast)
```javascript
ciyfn.toast('操作成功');
ciyfn.toast('操作失败', 'error');
```
#### 选项卡(ciyfn.tabcard)
```html
```
---
## 开发规范
### HTML结构规范
#### 标准页面结构
```html
```
#### 表单结构
```html
```
### JavaScript编码规范
#### 页面初始化
```javascript
'use strict';
var table;
ciyfn.pageload(function() {
// 初始化组件
ciycmp({
dom: '[com=status]',
range: 'status',
all: '全部'
});
// 初始化表格
table = new ciyclass.table({
dom: '.table',
url: 'api/list',
pagecount: 20
});
table.callpage(1);
});
```
#### 组件初始化
```javascript
// 标准初始化
ciycmp({
dom: '[com=component]',
range: 'dictionary',
value: '1',
onchange: function(e) {
console.log('changed:', e.value);
}
});
```
#### 事件处理
```javascript
// 使用$5绑定事件
$5('.btn-save').on('click', function(e) {
e.preventDefault();
// 保存逻辑
});
// 使用原生addEventListener
document.querySelector('.btn-save').addEventListener('click', function(e) {
e.preventDefault();
// 保存逻辑
});
```
#### API调用
```javascript
ciyfn.callfunc('api/admin.update', {
id: 1,
name: 'test'
}, function(json) {
//返回code:1,成功回调
});
```
#### 表单获取
```javascript
var form = ciyfn.getform('.search');
console.log(form);
```
### 样式规范
#### CSS变量
```css
:root {
/* 主色 */
--man3: #d7eeff;
--man4: #80c1f3;
--man5: #1E9FFF;
--man6: #1e89db;
--man7: #8568f7;
--mant: #ffffff;
/* 成功色 */
--succ5: #03a547;
--succ6: #048238;
--succt: #ffffff;
/* 警示色 */
--warn5: #e39725;
--warn6: #b97a1c;
--warnt: #ffffff;
/* 失败色 */
--dag5: #e34242;
--dag6: #bd2525;
--dagt: #ffffff;
/* 文字色 */
--txt1: #8c9ba4;
--txt2: #818e97;
--txt3: #738088;
--txt4: #646e76;
--txt5: #576067;
--txt6: #454d52;
--txt7: #2c3236;
--txt8: #060708;
--txt9: #000000;
/* 背景色 */
--bg1: #ffffff;
--bg2: #fbfbfc;
--bg3: #f7f8f8;
--bg4: #f0f2f2;
--bg5: #e3e6e7;
--bg6: #cdd2d4;
--bg7: #afb6b9;
--bg8: #939da1;
--bg9: #7e8a8e;
/* 其他css变量 */
--e-scroll: rgba(0, 0, 0, 0.2);
--e-tabselect: #fffec5;
--e-dialog: 2px 2px 20px -10px #000000;
--e-inputbg: #f7f7f7;
--e-inputbr: #ffffff;
--e-switchtxt: #2c3236;
--e-inputshadow: 0 1px 3px 0 #00000042;
--e-menusec: 0.5s;
}
```
#### 响应式断点
```css
@media (max-width: 767px) { /* 手机 */ }
@media (max-width: 991px) { /* 平板 */ }
@media (min-width: 576px) { /* 平板及以上 */ }
@media (min-width: 992px) { /* PC */ }
```
---
## 后端交互
### API调用规范
#### 标准请求格式
```javascript
ciyfn.callfunc('/admin/user.update', {
param1: 'value1',
param2: 'value2'
}, function(json) {
// 处理响应
});
```
#### 标准响应格式
```json
{
"code": 1,
"msg": "成功",
"data": {},
"list": [],
"count": 100,
"pageno": 1,
"once": {}
}
```
### 认证机制
框架使用JWT进行用户认证,Token存储在Cookie或LocalStorage中。
```
// 获取
var me = ciyfn.getstorage(ciy_vars.tokenfield);
```
### 权限控制
```
// 检查权限
if (ciyfn.nopower(me.power, 'p1v')) {
ciyfn.alert('未被授权');
}
```
权限格式:`.p1v.p2v.p3v.`(p=父权限,v=查看)
---
## 最佳实践
### 组件命名
- 使用 `com` 属性标识组件name
- 组件名采用下划线命名法
- 表单字段使用有意义的名称
```html
```
### 数据字典
字典数据通过 `range` 属性引用:
```javascript
// 字典结构
[{
id: 1,
name: '状态1',
upid: 0
}]
// 使用
ciycmp({
dom: '[com=status]',
range: 'userstatus' // 从localstorage缓存读取
});
```
### 事件处理
- 使用 `onchange` 处理组件值变化
- 事件回调接收统一的参数对象
```javascript
ciycmp({
dom: '[com=status]',
onchange: function(e) {
console.log(e.name); // 组件名称
console.log(e.value); // 组件值
console.log(e.dom); // DOM元素
console.log(e.from); // 触发来源
}
});
```
### 国际化
使用 `ciyfn.lang()` 函数实现多语言:
```javascript
console.log(ciyfn.lang('保存'));
console.log(ciyfn.lang('删除'));
```
### 性能优化
- 使用 `ciyfn.throttle()` 防抖
- 使用 `ciyfn.lazyimg()` 懒加载图片
- 列表分页加载
- 图片URL转换:使用 `ciyfn.file_stor()` 将数据库存储路径转换为云存储绝对URL
- 图片查看:使用 `ciyfn.showimg(index, imagesString)` 查看多张图片,参数为起始索引和用`~`连接的图片路径字符串
```javascript
// 图片URL转换
var imgurl = ciyfn.file_stor('/2024/03/14/image.jpg');
// 查看多张图片,用~分隔(数据库中的原始存储)
ciyfn.showimg(1, '/img/1.jpg~/img/2.jpg~/img/3.jpg'); // 从第2张开始查看
```
```javascript
// 防抖示例
$5('.search-input').on('input', function() {
if (ciyfn.throttle(this)) return;
// 搜索逻辑
});
```
---
## 开发流程
### 新建页面
1. 在 `web/xxx/` 下创建同名HTML文件和后端文件
2. 引入必要的JS和CSS文件
3. 使用组件标签构建页面
4. 编写初始化逻辑
5. 实现交互功能
6. 测试和优化
### 示例代码
#### 列表页面示例
```html
```
#### 表单页面示例
```html
```
---
## 常用API
### 工具函数
```javascript
// 全局函数
tostr(val, defval) // 转字符串
toint(val, def) // 转整数
tofloat(val, def) // 转浮点数
tostamp(time) // 转时间戳
isarray(v) // 判断是否Array类型
isobj(v) // 判断是否Object类型
iselement(v) // 判断是否Element类型
// DOM操作
$5(selector) // 选择元素
dom.css('color') // 获取样式
dom.css()['color'] // 获取计算后样式
dom.css(name, val) // 设置样式
dom.css({color:#xxx}) // 批量设置样式
dom.show() / dom.hide() // 显示/隐藏
dom.on(event, handler) // 绑定事件
dom.val(value) // 获取/设置值
// 数据处理
ciyfn.tojson(str) // JSON解析
ciyfn.jsontostr(obj) // JSON序列化
ciyfn.tostamp(time) // 转时间戳
ciyfn.todatetime(stamp, fmt) // 格式化日期
// 存储
ciyfn.getstorage(key) // 读取存储
ciyfn.setstorage(key, val) // 写入存储
// 消息提示
ciyfn.toast(msg, type) // 提示
ciyfn.alert(opn) // 弹窗
// API调用
ciyfn.callfunc(url, data, callback, opn) // 调用接口
```
### 表格API
```javascript
// 分页
table.callpage(page); // 加载页面
table.updateline(json); // 更新行
table.delline(json); // 删除行
// 搜索
table.search(dom, act); // 执行搜索
```
---
## 八、注意事项
1. **原生开发**: 尽量使用原生开发,已封装$5(jQuery改进版)
1. **严格模式**: 所有JS代码使用 `'use strict'`
3. **引号使用**: 代码中使用单引号
4. **事件处理**: 使用 `$5().on()` 或原生 `addEventListener`
5. **异步处理**: 使用回调函数处理异步结果
6. **数据验证**: 后端必须验证数据
7. **错误处理**: 无需处理API错误,自动弹窗。特殊情况,在opn参数中定义fail函数
8. **性能优化**: 大数据列表使用分页加载
9. **兼容性**: 确保IE11+兼容
10. **安全性**: 防止XSS和CSRF攻击
11. **菜单结构设计**: PC端菜单应具备清晰的层级结构,二次菜单设计必须有一级菜单。
---
## 常见问题
### Q: 组件不显示?
A: 检查是否正确引入了组件库文件(ciycmp.js、ciycmp2.js)。
### Q: 表格数据不加载?
A: 检查API返回格式是否符合标准,确认 `url` 参数正确。
## 参考资料
- 组件示例:`web/admin/demo/front/`
- API文档:参考各组件的源码注释
- 设计思路:参考框架注释中的设计说明
---
**版本**: 1.0.0
**作者**: Ciyon Team