ciyon_ai/aiskill/ciyon-PC前端.md
2026-04-15 17:28:46 +08:00

933 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
<ciy-datetime com="starttime" value="2026-01-01"></ciy-datetime>
<script>
ciycmp({
dom: '[com=starttime]',
type: 'datetime',
onchange: function(e) {
console.log('selected:', e.value);
}
});
</script>
```
#### 面向对象模式
使用类封装复杂功能:
```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
<ciy-datetime com="date1" type="date" value="2026-01-01"></ciy-datetime>
<ciy-datetime com="datetime1" type="datetime" value="2026-01-01 12:00"></ciy-datetime>
<ciy-datetime com="month1" type="month" value="2026-01"></ciy-datetime>
```
**属性说明**
- `com`: 组件名称(必填)
- `type`: 类型date/datetime/month
- `value`: 初始值(时间戳或日期字符串)
- `mindate`: 最小日期
- `maxdate`: 最大日期
- `placeholder`: 占位文本
#### 日期范围选择器ciy-daterange
```html
<ciy-daterange com="daterange1" type="date" value="2026-01-01~2026-12-31"></ciy-daterange>
```
**输出格式**`开始日期~结束日期`
#### 下拉选择框ciy-select
```html
<ciy-select com="status1" range="auditstatus" all="全部"></ciy-select>
<script>
ciycmp({
dom: '[com=status1]',
range: 'auditstatus', // 字典key
all: '全部', // 第一项
filter: {field: 'upid', value: 0}, // 过滤条件
onchange: function(e) {
console.log('selected:', e.value);
}
});
</script>
```
#### 多选下拉框ciy-selmulti
```html
<ciy-selmulti com="tags1" range="tags"></ciy-selmulti>
```
**输出格式**`,id1,id2,id3,`
#### 级联选择框ciy-selcas
```html
<ciy-selcas com="region1" range="region"></ciy-selcas>
```
用于省市区级联选择。
#### 开关ciy-switch
```html
<ciy-switch com="enable1" value="1"></ciy-switch>
```
**输出值**`1`(开启)/ `2`(关闭)
#### 单位编辑器ciy-inputunitedit
```html
<ciy-inputunitedit com="unit1" value="瓶|24|盒|20|箱"></ciy-inputunitedit>
```
用于三级单位换算1箱=20盒1盒=24瓶。
#### 地图选择器ciy-map
```html
<ciy-map com="location1" value="116.404,39.915"></ciy-map>
```
输出经纬度:`lng,lat`
#### 文本编辑器ciy-textarea
```html
<ciy-textarea com="content1" max="1000"></ciy-textarea>
```
支持Tab键、字数统计、@用户提示。
#### 文件上传ciy-upload
```html
<ciy-upload com="file1" type="jpg,png" maxcount="1"></ciy-upload>
```
**属性说明**
- `type`: 允许的文件类型
- `maxcount`: 最大上传数量
- `maxkb`: 文件大小限制KB
### 列表组件
#### 表格组件ciyclass.table
```html
<div class="table">
<div class="list">
<!-- 表格内容自动生成 -->
</div>
<div>
<div class="btmbtn"></div>
<div>
<div class="btmbtn"></div>
<div class="page"></div>
</div>
</div>
</div>
<script>
var table = new ciyclass.table({
dom: '.table',
url: 'list',
pagecount: 20,
fn_tdcontent: function(key, value, field, row, json) {
// 自定义单元格内容
if (key == 'status') {
return '<span class="tag">' + value + '</span>';
}
// 自定义操作按钮
if (key == '_btn') {
var html = '';
html += `<a class="btn def" onclick="menubtn(this, 'view')">查看</a>`;
// 根据需求添加或删除按钮
// html += `<a class="btn" onclick="menubtn(this, 'edit')">修改</a>`;
return html;
}
return value;
},
fn_done: function(json, post) {
// 数据加载完成回调
}
});
table.callpage(1);
</script>
```
**功能特性**
- 动态列配置
- 列宽调整记忆
- 列排序
- 列隐藏
- 行选择
- 分页
- 搜索
- 顶部选项卡筛选
**顶部选项卡筛选**:使用 `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
<div class="table">
<ul class="list row">
<!-- 卡片内容自动生成 -->
</ul>
<div>
<div class="btmbtn"></div>
<div class="page"></div>
</div>
</div>
<script>
var table = new ciyclass.cardtable({
dom: '.table',
url: 'api/list',
pagecount: 9,
fn_lihtml: function(ldat) {
return `<li data-id="${ldat.id}">
<div class="ciy-list">
<div class="l1">${ldat.name}</div>
<div class="l2">${ldat.memo}</div>
</div>
</li>`;
}
});
table.callpage(1);
</script>
```
### 功能组件
#### 弹窗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
<div class="ciy-tabcard">
<div class="tabs">
<div class="tab active">标签1</div>
<div class="tab">标签2</div>
</div>
<div class="contents">
<div class="content active">内容1</div>
<div class="content">内容2</div>
</div>
</div>
```
---
## 开发规范
### HTML结构规范
#### 标准页面结构
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/jscss/style.css" rel="stylesheet">
<script src="/jscss/theme.js"></script>
</head>
<body>
<div class="container">
<!-- 搜索表单 -->
<form class="search" onsubmit="table.search(this,'btn');return false;">
<div>
<div class="sinps">
<div class="ciy-form">
<label>状态</label>
<div><ciy-select com="status" range="status"></ciy-select></div>
</div>
</div>
<div class="sbtns">
<button class="btn" type="submit">查询</button>
<a class="btn" onclick="edit(0)">添加</a>
</div>
</div>
</form>
<!-- 列表区域 -->
<div class="table">
<div class="list"></div>
<div>
<div class="btmbtn"></div>
<div class="page"></div>
</div>
</div>
</div>
<script src="/jscss/ciy.js"></script>
<script src="/jscss/ciycmp.js"></script>
<script src="/jscss/ciycmp2.js"></script>
<script src="/jscss/ciytable.js"></script>
<script src="./common.js"></script>
<script>
// 页面逻辑
</script>
</body>
</html>
```
#### 表单结构
```html
<div class="ciy-form">
<label>字段名称</label>
<div>
<ciy-select com="field" range="dict"></ciy-select>
</div>
</div>
```
### 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
<ciy-select com="user_status" range="userstatus"></ciy-select>
<ciy-datetime com="start_time" type="datetime"></ciy-datetime>
```
### 数据字典
字典数据通过 `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
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="/jscss/style.css" rel="stylesheet">
</head>
<body>
<div class="container">
<form class="search" onsubmit="table.search(this,'btn');return false;">
<div>
<div class="sinps">
<div class="ciy-form">
<label>状态</label>
<div><ciy-select com="status" range="status" all="全部"></ciy-select></div>
</div>
</div>
<div class="sbtns">
<button class="btn" type="submit">查询</button>
<a class="btn" onclick="edit(0)">添加</a>
</div>
</div>
</form>
<div class="table">
<div class="list"></div>
<div>
<div class="btmbtn"></div>
<div class="page"></div>
</div>
</div>
</div>
<script src="/jscss/ciy.js"></script>
<script src="/jscss/ciycmp.js"></script>
<script src="/jscss/ciycmp2.js"></script>
<script src="/jscss/ciytable.js"></script>
<script src="./common.js"></script>
<script>
'use strict';
var table;
ciyfn.pageload(function() {
ciycmp({ dom: '[com=status]', range: 'status', all: '全部' });
table = new ciyclass.table({
dom: '.table',
url: 'list', //JS引擎将自动拼接为当前目录/文件名.list /admin/demopage.list
pagecount: 20,
fn_beforedata: function(json) {
ciyfn.fillsearch({ dom: '.search', data: json });
return json;
}
});
table.callpage(1);
});
function edit(id) {
ciyfn.alert({
title: id == 0 ? '添加' : '修改',
width: '600px',
frame: 'edit.html?id=' + id,
cb: function(opn) {
opn.close();
table.updateline(opn.inputs);
}
});
}
</script>
</body>
</html>
```
#### 表单页面示例
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="/jscss/style.css" rel="stylesheet">
</head>
<body>
<div class="container">
<form onsubmit="save(event);">
<div class="ciy-form">
<label>名称</label>
<div><input type="text" name="name" required></div>
</div>
<div class="ciy-form">
<label>状态</label>
<div><ciy-switch com="status" value="1"></ciy-switch></div>
</div>
<div class="ciy-form">
<label>时间</label>
<div><ciy-datetime com="time" type="datetime"></ciy-datetime></div>
</div>
<button class="btn" type="submit">保存</button>
</form>
</div>
<script src="/jscss/ciy.js"></script>
<script src="/jscss/ciycmp.js"></script>
<script src="./common.js"></script>
<script>
'use strict';
ciyfn.pageload(function() {
ciycmp({ dom: '[com=status]' });
ciycmp({ dom: '[com=time]', type: 'datetime' });
});
function save(event) {
event.preventDefault();
var postparam = ciyfn.getform(event.target);
ciyfn.callfunc('save', postparam, function(json) {
ciyfn.toast('保存成功');
});
}
</script>
</body>
</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