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

20 KiB
Raw Permalink Blame History

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操作接口

// 选择元素
var dom = $5('.class-name');

// 链式调用
dom.css({color: 'red'}).addClass('active').show();

// 事件绑定
dom.on('click', function(e) {
    console.log('clicked');
});

组件化模式

使用自定义标签和 ciycmp() 函数初始化组件:

<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>

面向对象模式

使用类封装复杂功能:

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

<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

<ciy-daterange com="daterange1" type="date" value="2026-01-01~2026-12-31"></ciy-daterange>

输出格式开始日期~结束日期

下拉选择框ciy-select

<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

<ciy-selmulti com="tags1" range="tags"></ciy-selmulti>

输出格式,id1,id2,id3,

级联选择框ciy-selcas

<ciy-selcas com="region1" range="region"></ciy-selcas>

用于省市区级联选择。

开关ciy-switch

<ciy-switch com="enable1" value="1"></ciy-switch>

输出值1(开启)/ 2(关闭)

单位编辑器ciy-inputunitedit

<ciy-inputunitedit com="unit1" value="瓶|24|盒|20|箱"></ciy-inputunitedit>

用于三级单位换算1箱=20盒1盒=24瓶。

地图选择器ciy-map

<ciy-map com="location1" value="116.404,39.915"></ciy-map>

输出经纬度:lng,lat

文本编辑器ciy-textarea

<ciy-textarea com="content1" max="1000"></ciy-textarea>

支持Tab键、字数统计、@用户提示。

文件上传ciy-upload

<ciy-upload com="file1" type="jpg,png" maxcount="1"></ciy-upload>

属性说明

  • type: 允许的文件类型
  • maxcount: 最大上传数量
  • maxkb: 文件大小限制KB

列表组件

表格组件ciyclass.table

<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>

功能特性

  • 动态列配置
  • 列宽调整记忆
  • 列排序
  • 列隐藏
  • 行选择
  • 分页
  • 搜索
  • 顶部选项卡筛选

顶部选项卡筛选:使用 fillsearchlidata 参数添加顶部选项卡,liall 设置"全部"选项的文本:

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

<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

ciyfn.alert({
    title: '提示',
    content: '操作成功',
    btn: ['确定', '取消'],
    cb: function(opn) {
        if (opn.btn == '确定') {
            // 确定操作
        }
    }
});

Toast提示ciyfn.toast

ciyfn.toast('操作成功');
ciyfn.toast('操作失败', 'error');

选项卡ciyfn.tabcard

<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结构规范

标准页面结构

<!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>

表单结构

<div class="ciy-form">
    <label>字段名称</label>
    <div>
        <ciy-select com="field" range="dict"></ciy-select>
    </div>
</div>

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);
});

组件初始化

// 标准初始化
ciycmp({
    dom: '[com=component]',
    range: 'dictionary',
    value: '1',
    onchange: function(e) {
        console.log('changed:', e.value);
    }
});

事件处理

// 使用$5绑定事件
$5('.btn-save').on('click', function(e) {
    e.preventDefault();
    // 保存逻辑
});

// 使用原生addEventListener
document.querySelector('.btn-save').addEventListener('click', function(e) {
    e.preventDefault();
    // 保存逻辑
});

API调用

ciyfn.callfunc('api/admin.update', {
    id: 1,
    name: 'test'
}, function(json) {
	//返回code:1成功回调
});

表单获取

var form = ciyfn.getform('.search');
console.log(form);

样式规范

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;
}

响应式断点

@media (max-width: 767px) { /* 手机 */ }
@media (max-width: 991px) { /* 平板 */ }
@media (min-width: 576px) { /* 平板及以上 */ }
@media (min-width: 992px) { /* PC */ }

后端交互

API调用规范

标准请求格式

ciyfn.callfunc('/admin/user.update', {
    param1: 'value1',
    param2: 'value2'
}, function(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
  • 组件名采用下划线命名法
  • 表单字段使用有意义的名称
<ciy-select com="user_status" range="userstatus"></ciy-select>
<ciy-datetime com="start_time" type="datetime"></ciy-datetime>

数据字典

字典数据通过 range 属性引用:

// 字典结构
[{
    id: 1,
    name: '状态1',
    upid: 0
}]

// 使用
ciycmp({
    dom: '[com=status]',
    range: 'userstatus'  // 从localstorage缓存读取
});

事件处理

  • 使用 onchange 处理组件值变化
  • 事件回调接收统一的参数对象
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() 函数实现多语言:

console.log(ciyfn.lang('保存'));
console.log(ciyfn.lang('删除'));

性能优化

  • 使用 ciyfn.throttle() 防抖
  • 使用 ciyfn.lazyimg() 懒加载图片
  • 列表分页加载
  • 图片URL转换使用 ciyfn.file_stor() 将数据库存储路径转换为云存储绝对URL
  • 图片查看:使用 ciyfn.showimg(index, imagesString) 查看多张图片,参数为起始索引和用~连接的图片路径字符串
// 图片URL转换
var imgurl = ciyfn.file_stor('/2024/03/14/image.jpg');

// 查看多张图片,用~分隔(数据库中的原始存储)
ciyfn.showimg(1, '/img/1.jpg~/img/2.jpg~/img/3.jpg');  // 从第2张开始查看
// 防抖示例
$5('.search-input').on('input', function() {
    if (ciyfn.throttle(this)) return;
    // 搜索逻辑
});

开发流程

新建页面

  1. web/xxx/ 下创建同名HTML文件和后端文件
  2. 引入必要的JS和CSS文件
  3. 使用组件标签构建页面
  4. 编写初始化逻辑
  5. 实现交互功能
  6. 测试和优化

示例代码

列表页面示例

<!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>

表单页面示例

<!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

工具函数

// 全局函数
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

// 分页
table.callpage(page);     // 加载页面
table.updateline(json);   // 更新行
table.delline(json);      // 删除行

// 搜索
table.search(dom, act);   // 执行搜索

八、注意事项

  1. 原生开发: 尽量使用原生开发,已封装$5(jQuery改进版)
  2. 严格模式: 所有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