chore: remove dist/ from git tracking, add to .gitignore

This commit is contained in:
yumoqing 2026-05-30 13:47:28 +08:00
parent db1934fd1c
commit fe98b07466
711 changed files with 1 additions and 122292 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dist/

View File

@ -1,12 +0,0 @@
https://cdn.jsdelivr.net/npm/xterm@4.19.0/css/xterm.css
https://vjs.zencdn.net/8.10.0/video-js.css
https://vjs.zencdn.net/ie8/1.1.2/videojs-ie8.min.js
https://cdn.jsdelivr.net/npm/xterm@4.19.0/lib/xterm.js
https://vjs.zencdn.net/8.10.0/video.min.js
# webrtc_adapter.js
come from https://github.com/googlecodelabs/webrtc-web
# peerjs
come from https://github.com/peers/peerjs
https://cdnjs.cloudflare.com/ajax/libs/peerjs/1.5.2/peerjs.min.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

26174
dist/3parties/hls.js vendored

File diff suppressed because it is too large Load Diff

2864
dist/3parties/marked.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

27427
dist/3parties/ort.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,215 +0,0 @@
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
/*
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree.
*/
'use strict';
var RTCPeerConnection = null;
var getUserMedia = null;
var attachMediaStream = null;
var reattachMediaStream = null;
var webrtcDetectedBrowser = null;
var webrtcDetectedVersion = null;
function maybeFixConfiguration(pcConfig) {
if (!pcConfig) {
return;
}
for (var i = 0; i < pcConfig.iceServers.length; i++) {
if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
delete pcConfig.iceServers[i].urls;
}
}
}
if (navigator.mozGetUserMedia) {
console.log('This appears to be Firefox');
window.webrtcDetectedBrowser = 'firefox';
window.webrtcDetectedVersion =
parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
// The RTCPeerConnection object.
RTCPeerConnection = function(pcConfig, pcConstraints) {
// .urls is not supported in FF yet.
maybeFixConfiguration(pcConfig);
return new mozRTCPeerConnection(pcConfig, pcConstraints);
};
// The RTCSessionDescription object.
RTCSessionDescription = mozRTCSessionDescription;
// The RTCIceCandidate object.
RTCIceCandidate = mozRTCIceCandidate;
// Get UserMedia (only difference is the prefix).
// Code from Adam Barth.
window.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
navigator.getUserMedia = getUserMedia;
// Creates iceServer from the url for FF.
window.createIceServer = function(url, username, password) {
var iceServer = null;
var urlParts = url.split(':');
if (urlParts[0].indexOf('stun') === 0) {
// Create iceServer with stun url.
iceServer = {
'url': url
};
} else if (urlParts[0].indexOf('turn') === 0) {
if (webrtcDetectedVersion < 27) {
// Create iceServer with turn url.
// Ignore the transport parameter from TURN url for FF version <=27.
var turnUrlParts = url.split('?');
// Return null for createIceServer if transport=tcp.
if (turnUrlParts.length === 1 ||
turnUrlParts[1].indexOf('transport=udp') === 0) {
iceServer = {
'url': turnUrlParts[0],
'credential': password,
'username': username
};
}
} else {
// FF 27 and above supports transport parameters in TURN url,
// So passing in the full url to create iceServer.
iceServer = {
'url': url,
'credential': password,
'username': username
};
}
}
return iceServer;
};
window.createIceServers = function(urls, username, password) {
var iceServers = [];
// Use .url for FireFox.
for (var i = 0; i < urls.length; i++) {
var iceServer = createIceServer(urls[i],
username,
password);
if (iceServer !== null) {
iceServers.push(iceServer);
}
}
return iceServers;
};
// Attach a media stream to an element.
window.attachMediaStream = function(element, stream) {
console.log('Attaching media stream');
element.mozSrcObject = stream;
element.play();
};
window.reattachMediaStream = function(to, from) {
console.log('Reattaching media stream');
to.mozSrcObject = from.mozSrcObject;
to.play();
};
} else if (navigator.webkitGetUserMedia) {
console.log('This appears to be Chrome');
window.webrtcDetectedBrowser = 'chrome';
// Temporary fix until crbug/374263 is fixed.
// Setting Chrome version to 999, if version is unavailable.
var result = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
if (result !== null) {
window.webrtcDetectedVersion = parseInt(result[2], 10);
} else {
window.webrtcDetectedVersion = 999;
}
// Creates iceServer from the url for Chrome M33 and earlier.
window.createIceServer = function(url, username, password) {
var iceServer = null;
var urlParts = url.split(':');
if (urlParts[0].indexOf('stun') === 0) {
// Create iceServer with stun url.
iceServer = {
'url': url
};
} else if (urlParts[0].indexOf('turn') === 0) {
// Chrome M28 & above uses below TURN format.
iceServer = {
'url': url,
'credential': password,
'username': username
};
}
return iceServer;
};
// Creates iceServers from the urls for Chrome M34 and above.
window.createIceServers = function(urls, username, password) {
var iceServers = [];
if (webrtcDetectedVersion >= 34) {
// .urls is supported since Chrome M34.
iceServers = {
'urls': urls,
'credential': password,
'username': username
};
} else {
for (var i = 0; i < urls.length; i++) {
var iceServer = createIceServer(urls[i],
username,
password);
if (iceServer !== null) {
iceServers.push(iceServer);
}
}
}
return iceServers;
};
// The RTCPeerConnection object.
RTCPeerConnection = function(pcConfig, pcConstraints) {
// .urls is supported since Chrome M34.
if (webrtcDetectedVersion < 34) {
maybeFixConfiguration(pcConfig);
}
return new webkitRTCPeerConnection(pcConfig, pcConstraints);
};
// Get UserMedia (only difference is the prefix).
// Code from Adam Barth.
window.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
navigator.getUserMedia = getUserMedia;
// Attach a media stream to an element.
window.attachMediaStream = function(element, stream) {
if (typeof element.srcObject !== 'undefined') {
element.srcObject = stream;
} else if (typeof element.mozSrcObject !== 'undefined') {
element.mozSrcObject = stream;
} else if (typeof element.src !== 'undefined') {
element.src = URL.createObjectURL(stream);
} else {
console.log('Error attaching stream to element.');
}
};
window.reattachMediaStream = function(to, from) {
to.src = from.src;
};
} else {
console.log('Browser does not appear to be WebRTC-capable');
}

View File

@ -1,2 +0,0 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(window,function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=0)}([function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){}return e.prototype.activate=function(e){this._terminal=e},e.prototype.dispose=function(){},e.prototype.fit=function(){var e=this.proposeDimensions();if(e&&this._terminal){var t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}},e.prototype.proposeDimensions=function(){if(this._terminal&&this._terminal.element&&this._terminal.element.parentElement){var e=this._terminal._core,t=window.getComputedStyle(this._terminal.element.parentElement),r=parseInt(t.getPropertyValue("height")),n=Math.max(0,parseInt(t.getPropertyValue("width"))),o=window.getComputedStyle(this._terminal.element),i=r-(parseInt(o.getPropertyValue("padding-top"))+parseInt(o.getPropertyValue("padding-bottom"))),a=n-(parseInt(o.getPropertyValue("padding-right"))+parseInt(o.getPropertyValue("padding-left")))-e.viewport.scrollBarWidth;return{cols:Math.max(2,Math.floor(a/e._renderService.dimensions.actualCellWidth)),rows:Math.max(1,Math.floor(i/e._renderService.dimensions.actualCellHeight))}}},e}();t.FitAddon=n}])});
//# sourceMappingURL=xterm-addon-fit.js.map

View File

@ -1,190 +0,0 @@
/**
* Copyright (c) 2014 The xterm.js authors. All rights reserved.
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* https://github.com/chjj/term.js
* @license MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*/
/**
* Default styles for xterm.js
*/
.xterm {
cursor: text;
position: relative;
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.xterm.focus,
.xterm:focus {
outline: none;
}
.xterm .xterm-helpers {
position: absolute;
top: 0;
/**
* The z-index of the helpers must be higher than the canvases in order for
* IMEs to appear on top.
*/
z-index: 5;
}
.xterm .xterm-helper-textarea {
padding: 0;
border: 0;
margin: 0;
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
position: absolute;
opacity: 0;
left: -9999em;
top: 0;
width: 0;
height: 0;
z-index: -5;
/** Prevent wrapping so the IME appears against the textarea at the correct position */
white-space: nowrap;
overflow: hidden;
resize: none;
}
.xterm .composition-view {
/* TODO: Composition position got messed up somewhere */
background: #000;
color: #FFF;
display: none;
position: absolute;
white-space: nowrap;
z-index: 1;
}
.xterm .composition-view.active {
display: block;
}
.xterm .xterm-viewport {
/* On OS X this is required in order for the scroll bar to appear fully opaque */
background-color: #000;
overflow-y: scroll;
cursor: default;
position: absolute;
right: 0;
left: 0;
top: 0;
bottom: 0;
}
.xterm .xterm-screen {
position: relative;
}
.xterm .xterm-screen canvas {
position: absolute;
left: 0;
top: 0;
}
.xterm .xterm-scroll-area {
visibility: hidden;
}
.xterm-char-measure-element {
display: inline-block;
visibility: hidden;
position: absolute;
top: 0;
left: -9999em;
line-height: normal;
}
.xterm.enable-mouse-events {
/* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
cursor: default;
}
.xterm.xterm-cursor-pointer,
.xterm .xterm-cursor-pointer {
cursor: pointer;
}
.xterm.column-select.focus {
/* Column selection mode */
cursor: crosshair;
}
.xterm .xterm-accessibility,
.xterm .xterm-message {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 10;
color: transparent;
}
.xterm .live-region {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
.xterm-dim {
opacity: 0.5;
}
.xterm-underline {
text-decoration: underline;
}
.xterm-strikethrough {
text-decoration: line-through;
}
.xterm-screen .xterm-decoration-container .xterm-decoration {
z-index: 6;
position: absolute;
}
.xterm-decoration-overview-ruler {
z-index: 7;
position: absolute;
top: 0;
right: 0;
pointer-events: none;
}
.xterm-decoration-top {
z-index: 2;
position: relative;
}

File diff suppressed because one or more lines are too long

0
dist/README.md vendored
View File

15667
dist/bricks.js vendored

File diff suppressed because it is too large Load Diff

876
dist/css/bricks.css vendored
View File

@ -1,876 +0,0 @@
html,
body {
height: 100%;
width: 100%;
margin: 0;
color: #8a8a8a;
background-color: #fafafa;
overflow: auto;
display: flex;
}
.responsive-img {
max-width: 100%; /* 限制最大宽度为容器 */
width: 100%; /* 占满容器宽度 */
height: auto; /* 高度自动,保持比例 */
display: block; /* 避免底部留空隙 */
object-fit: contain; /* 确保完整显示,而不是裁切 */
}
pre {
overflow-x: auto; /* 允许内容超出容器显示 */
background-color: #b5e5e5;
}
* {
box-sizing: border-box!important;
}
hr {
height: 1px;
background-color: #8a8a8a;
width: 80%;
}
.flexbox {
height: 100%;
width: 100%;
display: flex;
}
.curpos {
border-radius: 30%;
background-color: #f5f5f5;
}
.tabular {
border-radius: 8px;
padding: 5px;
margin: 5px;
background-color: #f5f5f5;
border: 1px solid #888888;
}
.card {
border-radius: 8px;
padding: 5px;
margin: 5px;
background-color: #f5f5f5;
border: 1px solid #888888;
}
.card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
border: 2px solid #ff8080;
}
.subcard {
background-color: #eeeeee;
}
.clickable {
color: #40cc40;
cursor: pointer;
}
.video-in-video {
position: relative;
}
.bigvideo {
position: absolute;
width: 100%;
height: 100%;
z-index: 0;
}
.smallvideo {
position: absolute;
bottom: 10px;
right: 30px;
width: 30%;
height: 30%;
z-index: 2;
}
.griddata {
display: grid;
grid-gap: 1px;
}
.resizebox {
width: 10px;
height: 10px;
background-color: darkblue;
position: absolute;
bottom: 0;
right: 0;
cursor: se-resize; /* 改变鼠标指针样式 */
}
.popup {
display: none;
position: absolute;
box-sizing: border-box; /* 包括边框在内计算宽度和高度 */
color: #111111;
background-color: #f1f1f1;
border: 1px solid #c1c1c1;
border-radius: 5px;
padding: 4px;
}
.titlebar {
background-color: #d8d8c8;
}
.toppopup {
box-shadow: 10px 5px 10px #000, 0 -5px 5px #fff;
}
.modal {
display:none;
position: absolute;
padding: 10px;
color: #111111;
background-color: #dddddd;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.menuitem {
backgroud-color: #eeeeee;
align-items: center;
border: 1px solid #ccc;
cursor: pointer;
}
.modal>.title {
background-color: #a0a0a0;
}
.message {
padding: 10px;
width: 30%;
height: 30%;
background-color: #f0f0f0;
color:#222222;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.error {
padding: 10px;
width: 30%;
height: 30%;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 10px;
}
.message>.title {
background-color: #3030f0;
color: #e8e8e8;
}
.error>.title {
background-color: #f03030;
color: #e8e8e8;
}
.vscroll {
overflow-x: scroll;
}
.hscroll {
overflow-y: scroll;
}
.scroll {
overflow: auto;
}
.vcontainer {
display: flex;
flex-direction: column;
}
.vbox {
display: flex;
flex-direction: column;
}
.hcontainer {
display: flex;
flex-direction: row;
}
.hbox {
display: flex;
flex-direction: row;
}
.fixitem {
flex:none;
}
.filler, .hfiller, .vfiller {
flex: auto;
flex-grow: 1;
overflow:hidden;
}
.vfiller .vbox:last-child {
overflow-x: overlay;
}
.vline {
width:1px;
height:100%;
background-colir:#999;
}
.hline {
height:1px;
width:100%;
background-colir:#999;
}
.hfiller::-webkit-scrollbar {
display: none;
}
.flc {
width: 203px;
overflow-y: scroll;
overflow-x: visible;
}
.vtoolbar {
heigth: 100%;
background-color: #f1f1f1;
border: 1px solid #ccc;
}
.selected {
background-color: #d4d4d4;
}
.htoolbar {
width: 100%;
height: 40px;
background-color: #f1f1f1;
border: 1px solid #ccc;
}
.toolbar-button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
border: 1px solid #888;
}
.toolbar-button-active {
background-color: #ddd;
}
.tabpanel {
background-color: #ededed;
border: 3px solid #888;
}
.tabpanel-content {
background-color: #f8f8f8;
border: 2px solid #888;
}
.multicolumns {
column-width: 340px;
colomn-gap: 10px;
overflow-x: none;
}
.selected_record {
border-radius: 8px;
border: 1px solid #f00;
}
.inputbox {
background-color: #f8f8f8;
color: #111111;
border: 1px solid #ccc;
padding: 10px;
margin: 0 0 1em 0;
}
.datagrid {
display:flex;
flex-direction:column;
width:100%;
height:100%;
}
.datagrid-grid {
width: 100%;
flex: 1;
overflow: auto;
display: flex;
flex-direction: row;
}
.datagrid-left {
height:100%;
display: flex;
flex-direction: column;
overflow: auto;
}
.datagrid-left>.scrollbar {
width:0px;
opacity:0;
}
.datagrid-right {
flex:1 0 ;
height:100%;
overflow: auto;
display: flex;
flex-direction: column;
}
.grid_header, .grid_footer {
height: 50px;
background-color: blue;
}
.childrensize {
display: flex;
flex-wrap: nowrap;
flex-shrink: 0;
}
.datagrid-row {
flex:0 0 150px;
display: flex;
flex-direction: row;
}
.datagrid-body {
width: 100%;
flex: 1;
overflow: auto;
display: flex;
flex-direction: column;
}
/* Flex 布局 */
.accordion {
display: flex;
flex-direction: column;
}
.accordion-item {
border: 1px solid #ccc;
width: auto;
margin-bottom: 5px;
}
.accordion-item:nth-child(odd) {
background-color: #fdfdfd;
width: auto;
}
.accordion-item:nth-child(even) {
background-color: #f9f9f9;
width: auto;
}
.accordion-item-selected {
background-color: #efefef;
}
.accordion-item-header {
padding: 10px;
background-color: #f0f0f0;
}
.accordion-item-info {
padding: 10px;
background-color: #fbfbfb;
cursor: pointer;
width: auto;
height: 50px;
}
.test_box {
height: 100px;
background-color: #e6e6e6;
border-radius: 5px;
flex-shrink:0;
border: 1px solid #c00;
}
.accordion-item-info-selected {
background-color: #e6e6e6;
}
.scrollpanel
.tabular-table {
width: 100%;
height: 100%;
overflow: auto;
}
.tabular-header-row {
display: flex;
top: 0;
position: sticky;
background-color: #dddddd;
min-width: 0;
min-width: fit-content;
flex-wrap: nowrap;
flex-shrink: 0;
}
.tabular-row {
display: flex;
margin-bottom: 5px;
min-width: 0;
min-width: fit-content;
flex-wrap: nowrap;
flex-shrink: 0;
}
.tabular-row:nth-child(odd) {
background-color: #efefef;
}
.tabular-row:nth-child(even) {
background-color: #f9f9f9;
}
.tabular-row-selected {
background-color: #fee2e2 !important;
color: #ef0000;
}
.tabular-row-content {
padding: 2;
}
.tabular-cell {
border: 1px solid #ccc;
overflow: hidden;
text-overflow: ellipsis;
}
.llm_msg {
margin-left: 5px;
margin-right: auto;
margin-bottom: 10px;
padding: 3px;
background-color:#fefedd;
border-top-left-radius: 10px;
border-top-right-radius: 0;
border-bottom-right-radius: 10px;
border-bottom-left-radius: 0;
box-shadow: 5px 5px 10px rgba(0, 0, 0.2, 0.5);
}
.user_msg {
margin-left: auto;
margin-right: 5px;
margin-bottom: 10px;
background-color:#ddfefe;
border-top-right-radius: 10px;
border-top-left-radius: 0;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 0;
box-shadow: 5px 5px 10px rgba(0.2, 0, 0, 0.5);
}
.llm_title {
background-color:#eeeeee;
}
.progress-container {
width: 80%;
background-color: #ddd;
border-radius: 5px;
overflow: hidden;
margin-top: 20px;
}
.progress-bar {
height: 30px;
width: 0%;
background-color: #4CAF50;
text-align: center;
color: white;
line-height: 30px;
}
.left {
justify-content: flex-start;
}
/* 居右 */
.right {
justify-content: flex-end;
}
/* 居中 */
.hcenter {
justify-content: center;
}
/* 居上 */
.top {
align-self: flex-start;
}
/* 居下 */
.bottom {
align-self: flex-end;
}
/* 居中 */
.vcenter {
align-self: center;
}
.video-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 12px;
background: #000;
}
.video-element {
width: 100%;
height: 100%;
border-radius: 12px;
display: block;
}
.controls {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
color: white;
font-size: 14px;
padding: 10px 15px;
transition: opacity 0.3s;
opacity: 0.9;
}
.controls:hover {
opacity: 1;
}
.progress-container {
margin-bottom: 10px;
}
.progress-bar {
width: 100%;
accent-color: #ff0000;
}
.controls-bottom {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.play-pause, .mute, .fullscreen {
background: none;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
}
.volume, .playback-speed, .audio-track-select {
font-size: 14px;
padding: 2px;
}
.time {
font-family: monospace;
}
.fullscreen {
margin-left: auto;
}
.thinking-content {
background-color: #fdfcf5;
}
.resp-error {
background-color: #f04444;
background-color: #f0f4f4;
}
.resp-content {
background-color: #f0eed8;
}
.droparea {
border: 2px dashed #666;
border-radius: 10px;
color: #666;
}
.droparea:hover {
border-color: #00aaff;
color: #00aaff;
background: #f0faff;
}
.mini-window {
width: 50px;
height: 50px;
}
.auto-textarea {
/* 1. 确保计算模型一致 */
box-sizing: border-box;
/* 2. 允许元素内部处理滚动事件,不传递给父层 */
touch-action: pan-y;
/* 3. 极其重要:有些 H5 框架会禁用默认滚动,这里强制开启 */
pointer-events: auto !important;
display: block;
width: 100%;
min-height: 40px; /* 初始高度 */
max-height: 350px; /* 最大高度:超过此高度将滚动 */
line-height: 1.5;
padding: 10px;
resize: none; /* 禁用右下角手动拉伸 */
overflow-y: hidden; /* 初始隐藏滚动条 */
overflow-x: hidden;
-webkit-appearance: none; /* 移除 iOS 默认内阴影 */
border: 1px solid #ccc;
border-radius: 4px;
outline: none;
transition: none;
}
/* 针对 Chrome/Edge/Safari 的滚动条美化(解决滚动条突然出现导致内容抖动) */
.auto-textarea::-webkit-scrollbar {
width: 5px;
}
.auto-textarea::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.2);
border-radius: 10px;
}
.inputbox:focus {
border-color: #007bff;
}
/* ========== Dark Theme (Sage Shell) ========== */
[data-theme="dark"] body,
[data-theme="dark"] {
color: #CBD5E1;
background-color: #0F172A;
}
[data-theme="dark"] .card {
background-color: #1E293B;
border: 1px solid #334155;
color: #E2E8F0;
}
[data-theme="dark"] .card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
border: 2px solid #60A5FA;
}
[data-theme="dark"] .subcard {
background-color: #1a2436;
}
[data-theme="dark"] .tabular {
background-color: #1E293B;
border: 1px solid #334155;
color: #CBD5E1;
}
[data-theme="dark"] .tabular-header-row {
background-color: #334155;
color: #F1F5F9;
}
[data-theme="dark"] .tabular-row:nth-child(odd) {
background-color: #1E293B;
}
[data-theme="dark"] .tabular-row:nth-child(even) {
background-color: #172033;
}
[data-theme="dark"] .tabular-row-selected {
background-color: #451a1a !important;
color: #F87171;
}
[data-theme="dark"] .tabular-cell {
border: 1px solid #334155;
color: #CBD5E1;
}
[data-theme="dark"] .curpos {
background-color: #1E293B;
}
[data-theme="dark"] .inputbox {
background-color: #1E293B;
color: #E2E8F0;
border: 1px solid #475569;
}
[data-theme="dark"] .inputbox:focus {
border-color: #3B82F6;
}
[data-theme="dark"] .popup {
background-color: #1E293B;
color: #E2E8F0;
border: 1px solid #475569;
}
[data-theme="dark"] .modal {
background-color: #1E293B;
color: #E2E8F0;
border: 1px solid #475569;
}
[data-theme="dark"] .modal>.title {
background-color: #334155;
color: #F1F5F9;
}
[data-theme="dark"] .message {
background-color: #1E293B;
color: #E2E8F0;
border: 1px solid #475569;
}
[data-theme="dark"] .vtoolbar,
[data-theme="dark"] .htoolbar {
background-color: #1E293B;
border: 1px solid #334155;
}
[data-theme="dark"] .toolbar-button {
border: 1px solid #475569;
color: #CBD5E1;
}
[data-theme="dark"] .toolbar-button-active {
background-color: #334155;
}
[data-theme="dark"] .tabpanel {
background-color: #1E293B;
border: 3px solid #475569;
}
[data-theme="dark"] .tabpanel-content {
background-color: #172033;
border: 2px solid #475569;
}
[data-theme="dark"] .selected {
background-color: #334155;
}
[data-theme="dark"] .accordion-item {
border: 1px solid #334155;
}
[data-theme="dark"] .accordion-item:nth-child(odd) {
background-color: #1E293B;
}
[data-theme="dark"] .accordion-item:nth-child(even) {
background-color: #172033;
}
[data-theme="dark"] .accordion-item-header {
background-color: #334155;
}
[data-theme="dark"] .accordion-item-info {
background-color: #1E293B;
}
[data-theme="dark"] .accordion-item-info-selected {
background-color: #334155;
}
[data-theme="dark"] .titlebar {
background-color: #334155;
}
[data-theme="dark"] .droparea {
border: 2px dashed #475569;
color: #94A3B8;
}
[data-theme="dark"] .droparea:hover {
border-color: #60A5FA;
color: #60A5FA;
background: #172033;
}
[data-theme="dark"] pre {
background-color: #1E293B;
color: #E2E8F0;
}
[data-theme="dark"] hr {
background-color: #475569;
}
[data-theme="dark"] .auto-textarea {
background-color: #1E293B;
color: #E2E8F0;
border: 1px solid #475569;
}
[data-theme="dark"] .thinking-content {
background-color: #1a2436;
}
[data-theme="dark"] .resp-content {
background-color: #1a2436;
}
[data-theme="dark"] .resp-error {
background-color: #7F1D1D;
}
[data-theme="dark"] .llm_msg {
background-color: #334155;
color: #E2E8F0;
}
[data-theme="dark"] .user_msg {
background-color: #1E3A5F;
color: #E2E8F0;
}
[data-theme="dark"] .llm_title {
background-color: #1E293B;
}
/* ---- Menu widget (language switcher, context menus) ---- */
[data-theme="dark"] .menuitem {
background-color: #334155 !important;
color: #E2E8F0 !important;
border: 1px solid #475569;
}
[data-theme="dark"] .menuitem:hover {
background-color: #475569 !important;
}
/* Override Menu widget's inline backgroundColor:"white" */
[data-theme="dark"] .popup .vbox,
[data-theme="dark"] .popup .vcontainer,
[data-theme="dark"] .popup .vscroll {
background-color: #1E293B !important;
color: #E2E8F0;
}
/* ---- Popup content text visibility ---- */
[data-theme="dark"] .popup h1,
[data-theme="dark"] .popup h2,
[data-theme="dark"] .popup h3,
[data-theme="dark"] .popup h4,
[data-theme="dark"] .popup h5,
[data-theme="dark"] .popup h6 {
color: #F1F5F9;
}
[data-theme="dark"] .popup .clickable {
color: #60A5FA;
}
/* ---- WindowsPanel (minimized windows dock) ---- */
[data-theme="dark"] .mini-window {
background-color: #1E293B;
border: 1px solid #475569;
}
[data-theme="dark"] .mini-window:hover {
border-color: #60A5FA;
}
/* ---- DynamicColumn ---- */
[data-theme="dark"] .griddata {
background-color: transparent;
}
/* ---- Top popup shadow (dark variant) ---- */
[data-theme="dark"] .toppopup {
box-shadow: 10px 5px 10px rgba(0,0,0,0.5), 0 -5px 5px rgba(255,255,255,0.05);
}

View File

@ -1,15 +0,0 @@
# bricks文档
像搭积木一样的编写前端应用bricks希望提供开发者所需的底层显示控件
开发应用时采用简单的组装和插拔方式完成应用的开发, bricks的API为json格式
不需要应用开发者掌握前端开发技术。
使用bricks开发应用的好处
* 质量和性能可控开发者开发的应用质量和性能依赖bricks提供的提供底层控件。
* 简单的开发方式,开发者以编写[json数据文件](descjson.md)的形式开发前端应用
## [bricks简介](brief.md)
## [bricks安装](install.md)
## [bricks开发](develop.md)
## [控件介绍](widgets.md)
## [控件描述文件](descjson.md)
## [事件处理](event.md)
## [对后台服务器要求](server.md)

View File

@ -1,205 +0,0 @@
# `bricks.Accordion` 技术文档
```markdown
# bricks.Accordion
`bricks.Accordion` 是一个基于 `bricks.VBox` 的可折叠面板组件,允许用户通过点击标题按钮切换显示不同的内容区域。常用于需要节省垂直空间、分组展示信息的界面设计中。
---
## 继承关系
- **继承自**: `bricks.VBox`
- **注册名称**: `'Accordion'`(通过 `bricks.Factory.register` 注册)
---
## 构造函数
### `new bricks.Accordion(opts)`
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项对象 |
#### `opts` 配置项
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `item_size` | String | `'25px'` | 每个标题项的高度CSS 尺寸字符串) |
| `item_css` | String | `'accordion-button'` | 标题按钮使用的 CSS 类名 |
| `content_css` | String | `'accordion-content'` | 内容区域使用的 CSS 类名 |
| `items` | Array\<Object\> | 必填 | 要显示的面板项列表 |
##### `items` 数组元素结构
每个 `item` 是一个对象,包含以下字段:
| 字段 | 类型 | 是否必需 | 说明 |
|------|------|----------|------|
| `name` | String | 是 | 唯一标识符,用于定位和缓存内容 |
| `icon` | String | 否 | 显示在按钮前的图标类名(如 Font Awesome 类) |
| `label` | String | 是 | 按钮上显示的文本标签 |
| `content` | Object | 是 | 描述子组件的配置对象(符合 `widgetBuild` 结构) |
| `refresh` | Boolean | 否 | 若为 `true`,每次点击都会重新构建内容(不使用缓存) |
---
## 成员属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `w_items` | Array\<bricks.Button\> | 存储所有标题按钮实例的数组 |
| `subcontents` | Object | 缓存已创建的内容组件,键为 `name`,值为对应的 widget 实例 |
| `content` | bricks.Filler | 包裹内容区域的容器 |
| `sub_container` | bricks.VScrollPanel | 实际承载内容的可滚动面板 |
| `key_select_items` | Array | 支持键盘导航的选择项集合(用于上下键选择) |
| `keyselectable` | Boolean | `true`,表示该组件支持键盘选择操作 |
---
## 方法
### `constructor(opts)`
初始化 Accordion 组件:
1. 调用父类构造函数。
2. 设置整体高度为 `100%`
3. 创建多个 `bricks.Button` 实例作为标题栏,并绑定 `click` 事件到 `change_content`
4. 初始化内容显示区域(`VScrollPanel`)并插入第一个项目的内容。
> ⚠️ 注意:第一个按钮会自动触发一次 `click` 事件以加载默认内容。
---
### `async change_content(event)`
处理标题按钮点击事件,动态切换显示内容。
#### 参数
- `event`: DOM 事件对象,`event.target.bricks_widget` 应指向被点击的 `bricks.Button` 实例。
#### 行为逻辑
1. 获取被点击按钮的 `name`
2. 在 `this.opts.items` 中查找对应项的位置与配置。
3. 判断是否需要刷新:
- 如果设置了 `refresh: true`
- 或者该内容尚未被缓存
4. 若需刷新或未缓存,则调用 `bricks.widgetBuild()` 异步生成新内容并缓存。
5. 清空当前内容区,并将新内容添加进 `sub_container`
6. 更新布局:移除旧的 `content` widget 并重新插入(确保位于正确位置)。
#### 错误处理
- 若找不到匹配的 `name`,会在控制台输出警告日志。
- 若 `item.content` 不存在,则打印错误信息并返回。
---
## 事件绑定
- 所有标题按钮均监听 `click` 事件,触发 `change_content` 回调。
- 使用 `.bind(this)` 确保回调中的 `this` 指向正确的 Accordion 实例。
---
## 示例配置
```javascript
var accordion = new bricks.Accordion({
item_size: '30px',
item_css: 'my-accordion-button',
content_css: 'my-accordion-content',
items: [
{
name: 'general',
icon: 'fa fa-home',
label: '通用设置',
content: {
widgettype: 'TextBlock',
text: '这里是通用设置内容...'
}
},
{
name: 'advanced',
icon: 'fa fa-cog',
label: '高级选项',
refresh: true,
content: {
widgettype: 'FormPanel',
fields: [ ... ]
}
}
]
});
```
---
## 样式建议CSS
```css
.accordion-button {
padding: 8px 12px;
background-color: #f0f0f0;
border-bottom: 1px solid #ddd;
cursor: pointer;
font-weight: bold;
}
.accordion-button:hover {
background-color: #e0e0e0;
}
.accordion-content {
padding: 10px;
background-color: #fff;
border: 1px solid #ddd;
min-height: 100px;
}
```
> 可根据实际需求覆盖默认类名或通过 `item_css` / `content_css` 自定义。
---
## 工厂注册
```javascript
bricks.Factory.register('Accordion', bricks.Accordion);
```
允许通过工厂方法创建实例:
```javascript
bricks.widgetBuild({ widgettype: 'Accordion', ... });
```
---
## 注意事项
- 内容组件是**懒加载**的,仅在首次点击时创建。
- 支持**缓存机制**,避免重复渲染提升性能;可通过 `refresh: true` 强制重载。
- 支持键盘导航(上下箭头选择按钮),前提是容器环境支持焦点管理。
- `widgetBuild` 返回的是 `Promise`,因此 `change_content` 定义为 `async` 函数。
---
## 调试信息
启用调试模式后,点击按钮时会输出类似日志:
```text
accordion: button= [Button Instance] name= general
```
可通过 `bricks.debug()` 控制开关。
---
```

View File

@ -1,226 +0,0 @@
# `bricks.AG_Grid` 技术文档
> 基于 [ag-Grid](https://www.ag-grid.com/) 封装的可复用 JavaScript 网格组件,继承自 `bricks.JsWidget`
---
## 概述
`bricks.AG_Grid` 是一个基于 `ag-Grid` 的封装类,用于在网页中快速创建功能丰富的数据表格。它支持从远程 URL 动态加载数据,并提供列定义、排序、过滤、多选、单元格点击事件等基础功能。
该类继承自 `bricks.JsWidget`,遵循统一的组件初始化和生命周期管理机制。
---
## 依赖
- `ag-Grid`(必须全局可用,即 `agGrid` 对象已加载)
- `bricks.js` 核心库(包含 `bricks.JsWidget` 基类)
---
## 构造函数
```js
new bricks.AG_Grid(options)
```
### 参数
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `options` | `Object` | ✅ | 配置选项对象 |
#### `options` 属性
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `dataurl` | `String` | ✅ | - | 数据接口 URL返回 JSON 格式数组数据 |
| `fields` | `Array<Object>` | ✅ | - | 列字段配置列表,每个字段包含显示信息 |
##### `fields` 字段配置项
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `name` | `String` | ✅ | - | 字段名(对应数据中的 key |
| `label` | `String` | ❌ | `name` | 表头显示名称 |
| `width` | `Number` | ❌ | `100` | 列宽度(像素) |
---
## 类定义
```js
var bricks = window.bricks || {};
bricks.AG_Grid = class extends bricks.JsWidget {
/*
opts 示例:
{
dataurl: "/api/data",
fields: [
{ name: "id", label: "ID", width: 80 },
{ name: "name", label: "姓名" },
{ name: "age", width: 60 }
]
}
*/
constructor(opts) {
super(opts);
this.ag_opts = {}; // ag-Grid 配置对象
}
init() {
// 初始化数据源
this.datasource = {
getRows: this.getRows.bind(this),
startRow: 0
};
// 设置 ag-Grid 配置项
this.ag_opts.columnDefs = [];
this.ag_opts.rowModelType = 'infinite';
this.ag_opts.maxConcurrentDatasourceRequests = 1;
this.ag_opts.datasource = this.datasource;
// 生成列定义
for (let i = 0; i < this.opts.fields.length; i++) {
const field = this.opts.fields[i];
const colDef = {
field: field.name,
headerName: field.label || field.name,
width: field.width || 100
};
this.ag_opts.columnDefs.push(colDef);
}
// 默认列行为:可排序、可过滤
this.ag_opts.defaultColDef = { sortable: true, filter: true };
// 启用多行选择
this.ag_opts.rowSelection = 'multiple';
// 启用行动画效果
this.ag_opts.animateRows = true;
// 绑定单元格点击事件
this.ag_opts.onCellClicked = this.cell_clicked.bind(this);
// 初始化 ag-Grid 实例
this.aggrid = new agGrid.Grid(this.dom_element, this.ag_opts);
// 加载初始数据(一次性加载全部)
fetch(this.opts.dataurl)
.then(response => response.json())
.then(data => {
this.ag_opts.api.setRowData(data); // 注意:此处与 infinite model 不符,见下方说明
})
.catch(err => console.error('Failed to load data:', err));
}
cell_clicked(params) {
bricks.debug('Cell clicked:', params);
}
}
```
---
## 方法说明
### `constructor(opts)`
调用父类构造函数并初始化 `this.ag_opts` 为空对象,用于后续存储 ag-Grid 配置。
### `init()`
组件初始化主逻辑:
1. 定义 `datasource` 对象(为无限滚动做准备,但当前实现未完全使用)。
2. 遍历 `opts.fields` 构建 `columnDefs`
3. 设置通用表格行为(排序、过滤、多选、动画)。
4. 创建 `ag-Grid` 实例并与 DOM 元素绑定。
5. 使用 `fetch` 请求 `dataurl` 获取初始数据,并通过 `setRowData` 填充表格。
> ⚠️ **注意**:虽然设置了 `rowModelType: 'infinite'`,但实际并未正确实现 `getRows` 方法,因此当前行为更像是“客户端模型”,而非真正的无限滚动。建议根据需求调整数据加载方式。
### `cell_clicked(params)`
单元格点击回调函数,输出调试信息到控制台(需 `bricks.debug` 存在)。
参数 `params` 包含:
- `value`: 当前单元格值
- `column`: 列对象
- `rowIndex`: 行索引
- `data`: 整行数据对象
- 更多详见 [ag-Grid 官方文档](https://www.ag-grid.com/javascript-data-grid/cell-events/)
---
## 使用示例
```html
<div id="myGrid" style="height: 400px;" class="ag-theme-alpine"></div>
<script>
new bricks.AG_Grid({
dom_element: document.getElementById('myGrid'),
dataurl: '/api/users',
fields: [
{ name: 'id', label: '用户ID', width: 80 },
{ name: 'name', label: '姓名', width: 120 },
{ name: 'email', label: '邮箱' },
{ name: 'age', label: '年龄', width: 60 }
]
});
</script>
```
---
## 已知问题与改进建议
| 问题 | 说明 | 建议 |
|------|------|------|
| `getRows` 未实现 | `datasource.getRows` 未定义,却赋值给了 `this.datasource.getRows` | 若使用 `infinite` 模式,应实现分页加载逻辑 |
| `startRow` 未绑定上下文 | `startRow``datasource` 中未正确定义为属性 | 应作为闭包变量或实例属性维护 |
| `setRowData` 不适用于 Infinite Model | `setRowData` 仅适用于客户端模型 | 改为调用 `api.setDatasource()` 并完整实现 `getRows` |
| 错误语法 | `maxConcurrentDatasourceRequests: 1,` 使用了冒号而非赋值 | 应为 `=` |
| `width``label` 的默认值写法错误 | 使用了位运算符 `|` 而非逻辑默认值 | 应使用 `||``??` |
### 修正建议代码片段
```js
// 正确的字段处理
width: field.width ?? 100,
headerName: field.label ?? field.name,
// 正确的属性赋值
this.ag_opts.maxConcurrentDatasourceRequests = 1;
// 实现 getRows若启用 infinite 模式)
getRows(params) {
fetch(`${this.opts.dataurl}?start=${params.startRow}&end=${params.endRow}`)
.then(res => res.json())
.then(data => {
params.successCallback(data, data.length === 100 ? 10000 : data.length);
});
}
```
---
## 总结
`bricks.AG_Grid` 提供了一个简洁的 ag-Grid 封装,适合快速集成静态或小规模动态数据表格。但在大数据场景下需完善 `infinite` 模式的数据源实现以提升性能。
建议后续优化方向:
- 完整支持分页/无限滚动
- 支持列类型、渲染器扩展
- 添加加载状态提示
- 支持外部刷新方法
---
📌 **版本**: 1.0
📅 **最后更新**: 2025-04

View File

@ -1,220 +0,0 @@
# `bricks.ASRClient` 技术文档
> 基于 WebSocket 的语音识别客户端组件,用于实时音频流传输与文本识别结果接收。
---
## 概述
`bricks.ASRClient` 是一个基于 `bricks.VBox` 的类,封装了浏览器端的语音识别功能。它通过调用 Web Audio API 获取用户麦克风输入,并使用 WebSocket 将音频数据以 Base64 编码形式发送至后端 ASR自动语音识别服务。同时监听服务器返回的识别结果并触发相应事件。
该组件提供了一个可点击的图标按钮来控制录音的开始与停止支持自定义图标、WebSocket 地址和附加参数。
---
## 继承关系
- **继承自**`bricks.VBox`
- **注册名称**`ASRClient`(可通过 `bricks.Factory.create('ASRClient', options)` 创建)
```js
bricks.Factory.register('ASRClient', bricks.ASRClient);
```
---
## 构造函数
### `constructor(opts)`
#### 参数
| 参数名 | 类型 | 说明 |
|----------------|----------|------|
| `opts` | Object | 配置选项对象,见下表 |
##### `opts` 配置项
| 属性名 | 类型 | 默认值 | 说明 |
|----------------|----------|--------|------|
| `start_icon` | String | `'imgs/start_recording.svg'` | 开始录音时显示的图标路径SVG/PNG |
| `stop_icon` | String | `'imgs/stop_recording.svg'` | 停止录音时显示的图标路径SVG/PNG |
| `ws_url` | String | 必填 | WebSocket 服务器地址,例如:`wss://asr.example.com/ws` |
| `icon_options` | Object | `{}` | 传递给内部 `bricks.Svg` 图标组件的额外配置 |
| `ws_params` | Object | `{}` | 发送至 WebSocket 服务的附加参数,在每次发送音频时合并使用 |
---
## 事件
`ASRClient` 支持以下自定义事件,可通过 `.bind()` 方法监听:
| 事件名 | 触发时机 | 参数格式(`event.params` |
|--------------|------------------------------|----------------------------|
| `start` | 用户点击开始录音 | 无参数 |
| `stop` | 用户点击停止录音 | 无参数 |
| `transtext` | 接收到服务器返回的识别文本 | `{ content, speaker, start, end }` |
示例:
```js
asr.bind('transtext', function(e) {
console.log('识别结果:', e.params.content);
});
```
---
## 属性
| 属性名 | 类型 | 说明 |
|---------------|---------------|------|
| `status` | String | 当前状态:`'start'``'stop'` |
| `icon` | `bricks.Svg` | 控制按钮图标实例 |
| `socket` | `WebSocket` | 连接到 ASR 服务的 WebSocket 实例 |
| `stream` | `MediaStream` | 来自 `getUserMedia` 的音频流 |
| `mediaRecorder` | `MediaRecorder` | 浏览器原生媒体录制对象,每秒收集并发送音频块 |
---
## 方法
### `toggle_button()`
切换录音状态(开始 ↔ 停止),并更新按钮图标。
- 若当前为 `'stop'`,则切换为 `'start'` 并调用 `start_recording()`
- 若当前为 `'start'`,则切换为 `'stop'` 并调用 `stop_recording()`
绑定在图标的 `click` 事件上。
---
### `async start_recording()`
启动麦克风录音并开始向服务器发送音频数据。
#### 行为流程:
1. 调用 `navigator.mediaDevices.getUserMedia({ audio: true })` 请求麦克风权限。
2. 创建 `MediaRecorder` 实例处理音频流。
3. 设置 `ondataavailable` 回调:每 1 秒生成一段音频 Blob。
4. 使用 `blobToBase64()` 工具将 Blob 转为 Base64 字符串。
5. 构造消息体并发送至 WebSocket 服务器:
```json
{
"type": "audiobuffer",
"data": "base64string...",
...ws_params
}
```
> ⚠️ 注意:需确保页面运行在 HTTPS 环境或本地开发环境localhost否则无法获取麦克风权限。
---
### `stop_recording()`
停止 `MediaRecorder` 录音,关闭音频流轨道。
```js
this.mediaRecorder.stop();
// 同时会释放麦克风资源
```
---
### `response_data(event)`
处理来自 WebSocket 的服务器响应。
#### 参数
- `event.data`: 服务器返回的 JSON 字符串
#### 动作
1. 解析 JSON 数据
2. 触发 `transtext` 事件并将解析后的数据作为参数分发
```js
this.dispatch('transtext', parsedData);
```
---
### `response_log(event)`
调试方法:将接收到的识别结果输出到控制台。
```js
console.log('response data=', event.params);
```
可通过重写此方法实现日志收集或 UI 更新。
---
## 内部依赖
| 工具/函数 | 说明 |
|----------------------|------|
| `bricks_resource(path)` | 解析资源路径的工具函数,通常用于构建静态资源 URL |
| `blobToBase64(blob)` | 异步将 Blob 转换为 Base64 编码字符串 |
| `objcopy(obj)` | 深拷贝对象(防止修改原始 `ws_params` |
> 示例 `blobToBase64` 实现:
> ```js
> function blobToBase64(blob) {
> return new Promise((resolve, reject) => {
> const reader = new FileReader();
> reader.onload = () => resolve(reader.result.split(',')[1]);
> reader.onerror = reject;
> reader.readAsDataURL(blob);
> });
> }
> ```
---
## 使用示例
```js
var asr = new bricks.ASRClient({
ws_url: 'wss://your-asr-server.com/asr',
start_icon: 'imgs/mic-on.svg',
stop_icon: 'imgs/mic-off.svg',
ws_params: {
lang: 'zh-CN',
model: 'conversational'
}
});
// 监听识别结果
asr.bind('transtext', function(e) {
document.getElementById('output').innerHTML += '<p>' + e.params.content + '</p>';
});
// 插入到 DOM 容器
document.body.appendChild(asr.render());
```
---
## 注意事项
1. **安全性要求**必须在安全上下文HTTPS 或 localhost中运行才能访问麦克风。
2. **浏览器兼容性**:需要支持 `MediaRecorder` API 和 `WebSocket`
3. **性能建议**:每秒发送一次音频块,避免频繁请求;建议使用 Opus 编码压缩音频。
4. **错误处理**:未对 `getUserMedia` 失败做降级处理,建议外层捕获异常。
---
## 版本信息
- **作者**Bricks Framework Team
- **版本**1.0
- **最后更新**2025年4月5日
---
📌 *文档结束*

View File

@ -1,404 +0,0 @@
# `bricks` 音频模块技术文档
本文档为 `bricks` 框架中的音频相关功能模块提供详细说明,包含以下核心类:
- `bricks.formatMs()` —— 时间格式化工具函数
- `bricks.AudioPlayer` —— 音频播放器组件
- `bricks.AudioRecorder` —— 音频录制与上传组件
- `bricks.TextedAudioPlayer` —— 带文本同步的流式音频播放器
- 工厂注册机制(通过 `bricks.Factory`
---
## 1. 工具函数:`bricks.formatMs(ms, all)`
将毫秒数转换为可读的时间字符串,支持自定义显示粒度。
### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `ms` | `number` | 时间(单位:毫秒) |
| `all` | `boolean?` | 是否强制显示所有时间单位即使高位为0 |
### 返回值
返回格式化的字符串,形式如:`"h:mm:ss″sss"``"mm:ss″sss"`
- 小时部分仅在非零时显示
- 分钟和秒根据上下文决定是否显示前导零
- 毫秒固定三位数字补全
### 示例
```js
bricks.formatMs(3661234); // 输出: "1:01:01″234"
bricks.formatMs(61234, true); // 输出: "01:01″0234"
```
---
## 2. 音频播放器:`bricks.AudioPlayer`
基于 HTML5 `<audio>` 元素封装的高级音频播放控制组件。
### 继承关系
```js
class AudioPlayer extends bricks.JsWidget
```
### 构造选项 (`options`)
| 属性 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `url` | `string` | 否 | `null` | 初始音频源 URL |
| `autoplay` | `boolean` | 否 | `false` | 是否自动播放 |
### 实例属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `audio` | `HTMLAudioElement` | 内部使用的 `<audio>` DOM 节点 |
| `source` | `HTMLSourceElement` | 动态创建的 `<source>` 节点 |
| `playlist` | `Array<string>` | 待播放的 URL 列表(队列) |
| `url_generator` | `AsyncGenerator` | 流式加载 URL 的异步生成器 |
| `srcList` | `Array<{played: boolean, url: string}>` | 动态加载的音频源列表 |
### 方法
#### `get_status() → string`
获取当前播放状态。
**返回值:**
| 状态 | 说明 |
|------|------|
| `"error"` | 音频加载出错 |
| `"ended"` | 播放结束 |
| `"paused"` | 暂停中 |
| `"loading"` | 数据不足,正在缓冲 |
| `"playing"` | 正在播放 |
---
#### `add_url(url: string)`
向播放队列添加新音频。若当前状态为 `'error'``'ended'`,立即播放该音频;否则加入队列。
---
#### `set_source(url: string)`
设置音频源并更新 DOM。
> ⚠️ 注意:会同时设置 `this.audio.src` 和内部 `<source>` 元素。
---
#### `set_stream_urls(response: Response)`
从服务器流式接收多个音频 URL每行一个用于连续播放场景如语音合成流
##### 参数
- `response`: `fetch` 返回的 `Response` 对象,其 body 应为逐行输出的文本流。
##### 实现逻辑
1. 创建异步生成器 `dyn_urls()` 解析流数据。
2. 使用 `schedule_once()` 异步启动 `load_queue_url()`
3. 自动预加载并顺序播放音频片段。
---
#### `async load_queue_url()`
内部方法:持续从 `url_generator` 获取新的音频 URL并维护 `srcList`
`srcList.length < 2` 时触发首次播放。
---
#### `async play_srclist(event?)`
播放 `srcList` 中尚未播放的第一个音频项。
- 若传入事件对象且音频未结束,则不执行。
- 播放完成后通过 `'ended'` 事件继续下一首。
---
#### `play()` / `toggle_play()`
- `play()`: 异步调用原生 `audio.play()`
- `toggle_play()`: 切换播放/暂停状态。
---
#### `set_url(url)`
设置音频源并立即开始播放。
等价于:
```js
this.set_source(url);
this.audio.play();
```
---
## 3. 音频录制器:`bricks.AudioRecorder`
实现麦克风录音、可视化、文件上传与下载功能。
### 外部依赖
必须引入第三方库 [Recorder.js](https://gitee.com/xiangyuecn/Recorder),支持 WAV 格式录音。
```html
<script src="https://cdn.jsdelivr.net/npm/recorder-js"></script>
```
### 继承关系
```js
class AudioRecorder extends bricks.HBox
```
### 构造选项 (`opts`)
| 属性 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `upload_url` | `string` | 否 | `null` | 录音结束后上传的目标地址 |
| `start_icon` | `string` | 否 | 内置 SVG | 开始按钮图标路径 |
| `stop_icon` | `string` | 否 | 内置 SVG | 停止按钮图标路径 |
| `icon_rate` | `number` | 否 | `undefined` | 图标缩放比例 |
### 事件系统
通过 `this.dispatch()` 触发以下自定义事件:
| 事件名 | 触发时机 | 携带参数 |
|--------|----------|---------|
| `record_started` | 开始录音 | 无 |
| `record_ended` | 结束录音 | `{data: Blob, url: string, duration: number}` |
| `uploaded` | 成功上传后 | 服务器返回结果 |
可通过 `bind()` 监听这些事件。
### 主要组件
- `rec_btn`: 使用 `bricks.Svg` 显示切换式按钮(开始/停止)
- `rec_time`: 显示已录制时长(调用 `bricks.formatMs()`
- `wave`: (预留)波形图显示区域(需额外包支持)
### 核心方法
#### `recOpen()` / `recClose()`
打开或关闭麦克风权限。
- 调用 `Recorder.open()` 请求用户授权。
- 成功后设置 `this.rec` 实例并派发 `record_started`
- 失败时打印调试信息。
---
#### `start_recording()` / `stop_recording()`
开始或停止录音。
- `start_recording`: 若已授权则直接开始,否则先调用 `recOpen()`
- `stop_recording`: 调用 `rec.stop()` 获取 `Blob` 和时长,生成本地 URL 并派发 `record_ended`
---
#### `on_process(buffers, powerLevel, ...)`
录音过程中的实时回调约每秒12次
当前用途:
- 更新录音时长显示(`bufferDuration`
- 可扩展用于绘制音量条或波形图(注释中示例)
---
#### `upload() → Promise<void>`
将录音文件以 `FormData` 形式上传至 `upload_url`
使用 `bricks.jpost()` 发送请求,附带运行中提示(`bricks.Running`)。
成功后派发 `uploaded` 事件。
---
#### `download()`
触发浏览器下载当前录音文件WAV 格式),文件名为 `recorder-{timestamp}.wav`
> ⚠️ 下载完成后自动释放 `ObjectURL` 防止内存泄漏。
---
## 4. 文本同步音频播放器:`bricks.TextedAudioPlayer`
专为“语音+字幕”场景设计的垂直布局播放器,支持流式 JSON 数据驱动。
### 继承关系
```js
class TextedAudioPlayer extends bricks.VBox
```
### 组件结构
- 上方:`AudioPlayer` 控件
- 下方:`VScrollPanel` 包含 `Text` 组件用于显示文本内容
### 核心特性
支持通过流式响应动态加载语音与对应文本,实现边生成边播放的效果(适用于 TTS + 字幕同步)。
### 方法
#### `set_stream_urls(response: Response)`
解析流式 JSON 响应,每一行为一个 `{audio: base64, text: string}` 对象。
使用辅助函数 `streamResponseJson(response, callback)` 逐条处理。
---
#### `load_stream_data(json)`
接收到单条流数据后的处理逻辑:
1. 存入 `streaming_buffer` 队列;
2. 若当前空闲(`wait_play === true`),立即触发 `playnext()`
---
#### `playnext()`
从缓冲区取出下一条数据:
- 提取 `audio` 字段并转为 URL`base64_to_url()`
- 设置播放器源并更新文本显示;
- 播放完毕后等待 `'ended'` 事件再次调用自身。
当数据耗尽时清空界面。
---
## 5. 工厂注册
所有组件均通过 `bricks.Factory` 注册,可在模板中使用标签方式实例化。
```js
bricks.Factory.register('AudioPlayer', bricks.AudioPlayer);
bricks.Factory.register('AudioRecorder', bricks.AudioRecorder);
bricks.Factory.register('TextedAudioPlayer', bricks.TextedAudioPlayer);
```
### 示例用法(假设支持声明式语法)
```html
<widget type="AudioPlayer" url="demo.mp3" autoplay="true"/>
<widget type="AudioRecorder" upload_url="/api/upload-audio"/>
<widget type="TextedAudioPlayer"/>
```
---
## 6. 辅助函数与约定
### `base64_to_url(base64str) → string`
将 Base64 编码的音频数据转换为 `ObjectURL`,供 `<audio>` 使用。
> ✅ 实际代码中需确保此函数存在。
---
### `schedule_once(fn, delay)`
延迟执行一次函数(通常为 `setTimeout(fn, delay * 1000)` 封装)。
---
### `bricks.debug(msg, ...args)`
框架级调试日志输出。
---
### `bricks.is_mobile() → boolean`
判断是否运行在移动设备上,影响下载行为处理。
---
## 7. 使用场景建议
| 场景 | 推荐组件 |
|------|----------|
| 普通音频播放 | `AudioPlayer` |
| 用户录音上传 | `AudioRecorder` |
| AI语音对话TTS+字幕) | `TextedAudioPlayer` + 流式 API |
| 连续播放多个短音频 | `AudioPlayer.set_stream_urls()` |
---
## 8. 注意事项
1. **跨域限制**:录音上传、音频源加载需注意 CORS。
2. **HTTPS 要求**:现代浏览器要求 `getUserMedia()` 在安全上下文中运行HTTPS 或 localhost
3. **移动端兼容性**iOS Safari 对自动播放和 `ObjectURL` 支持有限,建议用户交互后触发播放。
4. **内存管理**:使用 `URL.revokeObjectURL()` 及时释放资源。
5. **错误处理**:建议监听 `error` 事件并在 UI 中反馈。
---
## 9. 示例代码
### 初始化一个播放器
```js
const player = new bricks.AudioPlayer({
url: 'music.mp3',
autoplay: true
});
document.body.appendChild(player.dom_element);
```
### 创建录音器并绑定上传
```js
const recorder = new bricks.AudioRecorder({
upload_url: '/api/v1/audio-upload',
icon_rate: 1.2
});
recorder.bind('uploaded', (res) => {
console.log('Upload success:', res);
});
document.body.appendChild(recorder.dom_element);
```
### 流式语音播放AI 回复)
```js
fetch('/api/stream-tts', { method: 'POST', body: prompt })
.then(response => {
const tap = new bricks.TextedAudioPlayer();
tap.set_stream_urls(response);
document.body.appendChild(tap.dom_element);
});
```
---
> 📚 文档版本v1.0
> © 2025 bricks Framework Team

View File

@ -1,211 +0,0 @@
# `bricks.ChartBar` 技术文档
> 基于 ECharts 的柱状图组件,用于快速生成基于数据配置的柱状图表。
---
## 概述
`bricks.ChartBar``bricks` 框架中的一个图表扩展类,继承自 `bricks.EchartsExt`。它封装了使用 [Apache ECharts](https://echarts.apache.org/) 绘制**柱状图Bar Chart**所需的基本逻辑,支持通过配置项动态加载数据并渲染图表。
该组件适用于需要展示分类维度下多个指标对比的场景,例如销量统计、用户增长等。
---
## 类定义
```javascript
bricks.ChartBar = class extends bricks.EchartsExt
```
- **命名空间**: `bricks.ChartBar`
- **父类**: `bricks.EchartsExt`
- **注册名称**: `'ChartBar'`(通过 `bricks.Factory.register` 注册)
---
## 配置参数
`ChartBar` 支持以下初始化配置项:
| 参数 | 类型 | 说明 |
|------|------|------|
| `data_url` | `String` (可选) | 数据请求的 URL 地址。若提供,则通过 AJAX 获取数据。 |
| `data_params` | `Object` (可选) | 发送请求时携带的参数(与 `data_url` 配合使用)。 |
| `method` | `String` (可选) | 请求方法,默认为 `'GET'`,可设为 `'POST'` 等。 |
| `data` | `Array<Object>` (可选) | 直接传入的数据数组,格式为对象列表。优先级高于 `data_url`。 |
| `nameField` | `String` | 指定作为 X 轴分类字段的键名(如:地区、时间等)。 |
| `valueFields` | `Array<String>` | 指定作为系列Series值字段的键名数组即多个指标列。 |
### 示例数据结构
```json
[
{ "category": "A", "sales": 120, "profit": 80 },
{ "category": "B", "sales": 150, "profit": 90 }
]
```
对应配置:
```js
{
data: [...],
nameField: "category",
valueFields: ["sales", "profit"]
}
```
---
## 方法说明
### `values_from_data(data, name)`
从数据集中提取指定字段的所有值。
#### 参数
- `data`: `Array<Object>` - 数据源数组。
- `name`: `String` - 要提取的字段名。
#### 返回值
- `Array` - 包含所有 `data[i][name]` 值的数组。
#### 示例
```js
this.values_from_data([
{ year: '2020', sales: 100 },
{ year: '2021', sales: 130 }
], 'sales');
// → [100, 130]
```
---
### `lineinfo_from_data(data, name)`
构建单个 ECharts Series 配置对象(类型为 bar
#### 参数
- `data`: `Array<Object>` - 数据源。
- `name`: `String` - 字段名,用作 series 名称和数据提取依据。
#### 返回值
```js
{
name: String,
type: 'bar',
data: Array // 提取自 data 中该字段的值
}
```
#### 示例
```js
this.lineinfo_from_data(data, 'sales');
// → { name: 'sales', type: 'bar', data: [100, 130] }
```
---
### `setup_options(data)`
根据输入数据生成完整的 ECharts 配置选项(`option` 对象)。
#### 参数
- `data`: `Array<Object>` - 处理后的数据集。
#### 返回值 (`Object`)
返回标准的 ECharts 配置对象,包含:
```js
{
tooltip: { trigger: 'axis' },
legend: { data: [...] }, // 分类名称列表
xAxis: {
type: 'category',
data: [...] // nameField 对应的值列表
},
yAxis: {
type: 'value'
},
series: [ ... ] // 每个 valueField 对应一个 bar series
}
```
#### 内部逻辑
1. 提取 `nameField` 的值作为 X 轴和图例数据;
2. 遍历 `valueFields`,为每个字段创建一个柱状图系列;
3. 整合成最终的 `options` 对象供 ECharts 渲染使用。
---
## 使用示例
```js
var chart = new bricks.ChartBar({
data_url: '/api/sales-data',
nameField: 'month',
valueFields: ['income', 'expense']
});
chart.renderTo('#chart-container');
```
或直接传入数据:
```js
var chart = new bricks.ChartBar({
data: [
{ month: 'Jan', income: 5000, expense: 3000 },
{ month: 'Feb', income: 6000, expense: 3500 }
],
nameField: 'month',
valueFields: ['income', 'expense']
});
chart.renderTo(document.getElementById('main'));
```
---
## 图表渲染效果
- **X 轴**:显示 `nameField` 的值(类别轴)。
- **Y 轴**:数值轴,自动适配最大最小值。
- **Tooltip**:开启轴触发提示,鼠标悬停显示各系列数值。
- **Legend**:图例显示所有 `valueFields` 名称。
- **Series**:每个 `valueField` 显示为一组柱子(支持多组并列柱状图)。
---
## 注册机制
```js
bricks.Factory.register('ChartBar', bricks.ChartBar);
```
允许通过工厂模式创建实例:
```js
bricks.Factory.create('ChartBar', config);
```
---
## 注意事项
1. 必须确保页面已引入 ECharts 库且 `bricks.EchartsExt` 已正确定义。
2. `nameField``valueFields` 必须存在于数据对象中。
3. 若同时设置了 `data``data_url`,将优先使用 `data`
4. 支持异步数据加载,`data_url` 返回的数据应为 JSON 数组。
---
## 版本信息
- **作者**: bricks framework team
- **版本**: 1.0.0
- **依赖**: `ECharts`, `bricks.EchartsExt`
---
📌 *文档生成时间2025-04-05*

View File

@ -1,276 +0,0 @@
# Bricks 流媒体传输模块技术文档
---
## 概述
`bricks.UpStreaming``bricks.down_streaming` 是用于实现浏览器端流式数据上传与下载的 JavaScript 工具类,基于现代 Web API`ReadableStream``fetch`)构建。该模块适用于需要实时上传二进制数据(如音频、视频流)或处理响应流的场景。
主要功能包括:
- **上行流UpStreaming**:通过 `POST` 请求将数据以 `application/octet-stream` 格式分块发送至指定 URL。
- **下行流解析器down_streaming**:异步生成器函数,用于消费 `Response.body` 的流式内容,并将其转换为字符串输出。
---
## 依赖说明
本模块依赖以下现代浏览器特性:
- `ReadableStream`
- `Headers`
- `fetch()` API支持 `duplex: 'full'`
- `async/await``async generator` 函数
- `TextDecoder`(隐含在 `String.fromCharCode` 使用中)
> ⚠️ 注意:`duplex: 'full'` 是处理流式请求所必需的,目前仅在部分现代浏览器(如 Chrome 105+)中支持。
---
## 命名空间初始化
```js
bricks = window.bricks || {};
```
确保 `bricks` 全局命名空间存在,避免覆盖已有定义。
---
## 类:`bricks.UpStreaming`
继承自 `bricks.JsWidget`,提供流式上传能力。
### 继承关系
```js
class UpStreaming extends bricks.JsWidget
```
> 需确保 `bricks.JsWidget` 已正确定义并可用。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 配置对象,必须包含 `url` 字段 |
示例配置:
```js
{
url: "https://example.com/upload"
}
```
#### 实现逻辑
调用父类构造函数 `super(opts)`,初始化组件基础属性。
---
### 方法
#### `async go()`
启动流式上传请求。
##### 返回值
- `{Promise<Response>}`:返回一个 resolve 为 `Response` 对象的 Promise。
##### 实现细节
1. 创建一个可读流 `this.body`使用当前实例作为其源source
2. 设置请求头:
- `Content-Type: application/octet-stream`
3. 使用 `fetch` 发起 POST 请求:
```js
fetch(this.url, {
method: 'POST',
headers: this.headers,
duplex: 'full',
body: this.body
})
```
4. 返回响应 `Response` 对象。
> 💡 `duplex: 'full'` 表示客户端可以同时写入请求体并读取响应,常用于流式接口。
---
#### `send(data)`
向流中写入一段数据。
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `data` | ArrayBuffer / Uint8Array 等 | 要发送的二进制数据块 |
##### 实现
调用内部控制器的 `enqueue` 方法将数据推入流队列:
```js
this.stream_ctlr.enqueue(data);
```
> 必须在调用 `start()` 后才能使用此方法。
---
#### `finish()`
关闭写入流,表示所有数据已发送完毕。
##### 实现
```js
this.stream_ctlr.close();
```
通知流结束,触发底层请求完成。
---
#### `start(controller)`
流控制器初始化钩子(由 `ReadableStream` 调用)。
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `controller` | ReadableStreamController | 浏览器提供的流控制对象 |
##### 实现
保存控制器引用以便后续调用:
```js
this.stream_ctlr = controller;
```
> 此方法是 `ReadableStream` 构造时自动调用的标准接口。
---
## 函数:`bricks.down_streaming`
异步生成器函数,用于逐段读取响应流并解码为字符串。
```js
bricks.down_streaming = async function*(response)
```
### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `response` | Response | 来自 `fetch` 的响应对象 |
### 返回值
- `{AsyncGenerator<string>}`:异步生成器,每次 `yield` 一个字符串片段。
### 实现逻辑
1. 若 `response` 为空,则直接返回。
2. 获取响应体的读取器:`response.body.getReader()`
3. 循环调用 `reader.read()` 直到流结束(`done === true`)。
4. 将每一块 `Uint8Array` 数据转换为字符串:
```js
String.fromCharCode(...value.value)
```
> ❌ 注意:这种方式对非 ASCII 字符(如中文)可能出错,推荐使用 `TextDecoder`
5. 输出日志并 `yield` 结果。
### 示例用法
```js
for await (const chunk of bricks.down_streaming(resp)) {
console.log('Received:', chunk);
}
```
---
## 使用示例
### 上行流发送数据
```js
const uploader = new bricks.UpStreaming({
url: '/api/stream-upload'
});
// 启动上传
const respPromise = uploader.go();
// 发送若干数据块
uploader.send(new TextEncoder().encode("Hello"));
uploader.send(new TextEncoder().encode("World"));
// 完成上传
uploader.finish();
// 获取响应
const resp = await respPromise;
console.log('Upload complete:', resp.status);
```
### 下行流接收数据
```js
const response = await fetch('/api/stream-download');
for await (const textChunk of bricks.down_streaming(response)) {
console.log('Chunk:', textChunk);
}
```
---
## 注意事项与优化建议
1. ✅ **兼容性警告**
`duplex: 'full'` 并非所有浏览器都支持,请在使用前检测环境支持情况。
2. ⚠️ **字符编码问题**
当前行内使用 `String.fromCharCode` 解码字节数组,**不支持 UTF-8 多字节字符**。
建议改进为使用 `TextDecoder`
```js
const decoder = new TextDecoder('utf-8');
let result = decoder.decode(value.value);
```
3. 🔒 **错误处理缺失**
当前代码未捕获 `fetch``reader.read()` 中可能出现的异常,建议添加 `try-catch`
4. 📦 **内存管理**
大量频繁调用 `send()` 可能导致内存堆积建议结合背压机制backpressure进行流控。
---
## 总结
| 功能 | 类/函数 | 用途 |
|------|--------|------|
| 流式上传 | `bricks.UpStreaming` | 实现双向流上传数据 |
| 流式下载解析 | `bricks.down_streaming` | 异步解析响应流为字符串 |
该模块适用于实时音视频通信、大文件分片上传、AI 流式推理等场景,具备良好的扩展潜力。
---
> 文档版本v1.0
> 最后更新2025-04-05

View File

@ -1,120 +0,0 @@
# Bricks.js 技术文档
> **Bricks.js** 是一个基于 JavaScript 的开发的前端组件化框架,用于构建可扩展、模块化的 Web 应用程序。bricks提供开发者使用json数据开发前端界面的能力它支持动态组件加载、事件绑定、数据实时获取、弹窗管理、国际化i18n、媒体流控制等功能。
bricks以控件为单位构建界面所有控件都从一个JsWidget继承而来bricks的控件分为普通控件和容器控件容器控件可以有子控件而普通控件没有子控件在bricks的源码文件中用”subwidgets“数组描述子控件
## bricks开发逻辑
使用json格式的原文件以".ui“结尾来开发前端界面每个ui文件
bricks使用规范化的json来描述前端界面json数据约束如下
{
"id" # 控件id
"widgettype" # 控件类型请看考bricks控件清单
"options" # 控件构造参数,支持控件类继承的参数
"subwidgets" # 容器类控件需要数组形式的一到多个控件json
"binds" # 事件处理数组, 参看bind数据规范
}
当widgettype值为"urlwidget“时options必须为
{
"url" # 从服务器中获取控件json
“params" # 参数
"method" # 缺省为“GET”
}
意思就是从服务器中用options中提供的数据从远程服务器中获取bricks控件源码渲染到target定义的容器控件中这种形式提供了bricks模块化编程能力其他的widgettype值必须时bricks中注册过的控件名称请参看bricks的控件清单
### binds数据规范
binds数组定义用户交互行为每个绑定bind描述符包含以下结构。
| 字段名 | 类型 | 必填 | 描述 |
|------------------|----------|------|------|
| `actiontype` | String | ✅ | 动作类型:`bricks`, `urlwidget`, `method`, `script`, `registerfunction`, `event`, `newwindow` |
| `wid` | String | ✅ | 触发事件的组件 ID |
| `event` | String | ✅ | 监听的事件名称,如 `'click'` |
| `target` | String 或 Widget | ✅ | 执行目标组件或特殊值(如 `'Popup'` |
| `datawidget` | String | ❌ | 获取运行时数据的源组件 ID |
| `datamethod` | String | ❌ | 数据获取方法,默认 `'getValue'` |
| `dataparams` | Object | ❌ | 调用 `datamethod` 的参数 |
| `datascript` | String | ❌ | 自定义脚本获取数据 |
| `rtdata` | Object | ❌ | 静态运行时数据 |
| `conform` | Object | ❌ | 确认对话框配置,在执行前弹出确认窗口 |
---
各类型绑定的特定属性
#### `urlwidget` action
从远程 URL 加载组件描述并渲染。
| 属性 | 类型 | 描述 |
|--------|---------|------|
| `mode` | String | `'replace'`, `'insert'`, `'append'`,默认 `'replace'` |
| `options.method` | String | HTTP 方法,默认 `'GET'` |
| `options.params` | Object | 请求参数 |
| `options.url` | String | 远程组件 JSON 地址 |
#### `bricks` action
本地创建 Bricks 组件。
| 属性 | 类型 | 描述 |
|--------|---------|------|
| `mode` | String | 同上 |
| `options| 对象 | 符合控件源码要求的json数据其属性widgettype的值必须时bricks已经注册的控件 |
#### `method` action
调用目标组件的方法。
| 属性 | 类型 | 描述 |
|---------|----------|------|
| `method` | String | 方法名 |
| `params` | Object | 方法参数kwargs |
#### `script` action
执行内联脚本。
| 属性 | 类型 | 描述 |
|---------|----------|------|
| `script` | String | 可执行 JS 代码字符串(异步函数体) |
| `params` | Object | 传入脚本的参数 |
#### `registerfunction` action
调用已注册的全局函数。
| 属性 | 类型 | 描述 |
|----------|----------|------|
| `rfname` | String | 注册函数名称 |
| `params` | Object | 函数参数 |
#### `event` action
在target控件上派发自定义事件。
| 属性 | 类型 | 描述 |
|------------------|----------|------|
| `dispatch_event` | String | 要派发的事件名 |
| `params` | Object | 携带的数据 |
---
### bricks 控件注册机制
bricks使用控件注册机制来实现识别源码中widgettype的值对应bricks的哪个控件。
注册实现
bricks.Factory.register(控件名, bricks实现的控件对象)完成注册
### bricks的已实现控件清单
已实现的[控件清单](widgets.md)
### 控件扩展要求
* javascript class类
* 继承自JsWidget或其子类
* 为控件命名,并注册此控件

View File

@ -1,60 +0,0 @@
# BricksApp
BricksApp是Bricks框架的应用类 BricksApp用来初始化Bricks环境创建Bricks页面的根控件
并将生成的根控件插入的全局控件Body中
Body对应的dom_element为页面的“body”元素。
后续可以通过bricks_app全局变量来引用BricksApp实例。
## 构建选项
```
opts = {
login_url:
"charsize:
"language":
"i18n":{
"url":'rrr',
"default_lang":'en'
},
"widget":{
"widgettype":"Text",
"options":{
}
}
}
```
### login_url
 登录url按照后台设置当需要访问受控url时会从此URL返回的json数据创建登录窗体用户登录
### chartsize
字符大小缺省16px
### languange
页面使用的语言,两个字符的语言
### i18n
定义国际化数据获取路径url按照GET方式language作为参数像后台获取改语言的json格式的翻译数据。
### widget
根控件描述对象
## 属性
### opts
保存创建选项
### login_url
用于构建登录控件的url
### charsize
字符大小所有输入控件Text IconTitle都受此影响控件大小。
### lang
前端界面语言,选项指定或获取缺省语言
### textList
### i18n
### session_id
## 方法
## 事件

View File

@ -1,106 +0,0 @@
# bricks框架简介
## 目录
* bricks目标
* bricks概念
* bricks开发方法
* bricks运行
## bricks目标
* 无前端代码或极少代码
* 降低前端开发技术难度
* 数据驱动
* 常用控件包装
* 纯json开发
## bricks概念
* 控件与控件继承
* 事件以及事件处理
* 控件嵌套和页面组装
### 控件与控件继承
bricks采用控件这一概念来描述web GUI的显示部件每个控件均映射到一个html
的标签类型的一个javascript类。每个控件均可以实例化并可在页面显示。
控件分为:基本控件,容器控件。
* 基本控件
基本控件是一个原子控件,不能有子控件。
* 容器控件
容器控件可以有子控件bricks通过在容器控件添加子控件以及在子容器控件中
在添加子子控件的方式来构造复杂的web页面。
控件的详细介绍请参看[控件说明](widgets.md)
### 控件扩展
如果现有的控件没法满足系统要求bricks支持控件扩展控件扩展需遵守
* 控件class继承自某一个控件的class
* 按照需求实现控件逻辑
* 在需要的地方用this.dispatch触发此控件的事件
假设需要扩展一个名字叫ExtContainer的控件
```
bricks.ExtContainer = class extends bricks.VBox {
constructor(opts){
super(opts);
/* 新控件的创建代码 */
}
......
/* 对象的其他方法在需要的时候在某个方法中使用this.dispatch('new_event', data)方法引发事件 */
}
bricks.register('ExtContainer', bricks.ExtContainer); /* 注册新控件 */
```
新控件的使用example.ui
```
{
"widgettype":"ExtContainer",
"options":{
....
},
"subwidgets":[
...
],
"binds":[
{
"wid":"self",
"event":"new_event",
"actiontype":"urlwidget",
"target":"some_container",
"options":{
"url":"{{entire_url('./some_ui.ui')}}"
}
}
]
}
```
### 事件以及事件处理
每个控件都能触发所映射dom元素的事件以及控件js类的成员函数以及祖先类的
成员函数中dispatch出的事件
所以bricks控件的事件来源于两类dom元素原生事件以及控件类中创造的事件。
两类事件处理方式相同。
### 控件表达形式
在服务器的后台以json文件的形式表达控件每个ui文件定义一个控件
对于容器控件可以在ui文件中的subwidgets子属性中为此控件添加子控件
#### id属性
字符串属性定义控件的id让控件可以用getWidgetById找到如果不给定系统会自动生成一个id
#### options属性
字典属性,创建控件时的选项,每个控件可接受的选项请参看控件选项说明
#### binds属性
数组属性定义零到多个事件响应每个bind字典需要遵守[事件](event.md)要求
#### 容器控件特有属性
##### subwidgets
数组属性,定义容器控件的子控件,每个元素定义一个子控件,子控件遵守控件的数据要素要求
## 开发
使用存放在服务器后台的.ui后缀的json格式文件来开发每个.ui文件定义一个控件 支持基本控件和容器空间。
关于如何书写ui文件请参考[UI文件格式](descjson.md)
## 调试
ui文件可以直接调试如在服务器根目录下的test目录下有一个hello.ui文件
就可以在浏览器中用urlhttps://sername/test/hello.ui调试

View File

@ -1,254 +0,0 @@
# `bricks.Button` 技术文档
> 继承自:`bricks.Layout`
`bricks.Button` 是一个可交互的按钮组件,支持图标、文本标签、自定义样式以及点击事件处理。它基于 Flexbox 布局进行内容排列,适用于水平或垂直方向的内容展示。
---
## 目录
- [概述](#概述)
- [配置选项Options](#配置选项options)
- [结构与布局](#结构与布局)
- [方法说明](#方法说明)
- [`constructor(opts)`](#constructoro)
- [`create()`](#create)
- [`opts_setup()`](#opts_setup)
- [`target_clicked(event)`](#target_clickedevent)
- [事件机制](#事件机制)
- [注册信息](#注册信息)
---
## 概述
`bricks.Button` 提供了一个封装良好的按钮控件,可用于触发操作或导航。该组件可以包含:
- 图标(通过 `bricks.Icon`
- 文本标签(通过 `bricks.Text`
- 自定义 CSS 样式
- 工具提示tooltip
- 可配置的布局方向(横向/纵向)
所有子元素使用 Flexbox 排列,并可通过 `item_rate` 控制尺寸比例。
---
## 配置选项Options
| 属性名 | 类型 | 描述 |
|----------------|----------|------|
| `orientation` | String | 布局方向,可选 `'horizontal'` 或默认为垂直 `'vertical'`。影响内部图标的排列方式。 |
| `height` | CSS value | 容器高度,默认 `100%`(由父级控制)。 |
| `width` | CSS value | 容器宽度,默认 `100%`。 |
| `item_rate` | Number | 子元素(图标/文字)的缩放比率,默认为 `1`。用于调整大小。 |
| `tooltip` | String | 鼠标悬停时显示的提示文本,设置到 DOM 的 `title` 属性上。 |
| `color` | String | 文字颜色(传递给 `Text` 组件)。 |
| `bgcolor` | String | 背景颜色(传递给 `Text` 组件)。 |
| `nonepack` | Boolean | 是否移除内边距和边框;若为 `true`,则 padding 设为 `0px`border 为 `0`。 |
| `name` | String | 组件名称,用作 DOM 元素 ID 的基础。 |
| `icon` | String | 图标资源 URL 或路径,用于创建 `bricks.Icon` 实例。 |
| `label` | String | 显示的文字标签支持国际化i18n。 |
| `css` | Object | 自定义 CSS 样式对象,将被合并到 DOM 元素的 `style` 中。 |
| `action` | Object | 点击按钮后执行的动作配置,详见下表。 |
### `action` 对象结构
| 子属性 | 类型 | 描述 |
|----------------|----------|------|
| `target` | String | 目标组件或模块名称。 |
| `datawidget` | String | 数据源控件名。 |
| `datamethod` | String | 数据获取方法名。 |
| `datascript` | String | 自定义脚本逻辑(如函数体字符串)。 |
| `dataparams` | Object | 发送至数据接口的参数。 |
| `rtdata` | Boolean | 是否实时刷新数据。 |
| `actiontype` | String | 动作类型,例如 `"navigate"``"submit"` 等。 |
| `...` | Any | 其他扩展字段,根据业务需求定义。 |
---
## 结构与布局
- 使用 `<button>` 元素作为根节点。
- 内部采用 `display: flex` 进行布局:
- 水平布局:`flex-direction: row`
- 垂直布局:`flex-direction: column`
- 支持图标在上、文字在下的垂直排布,或并排的水平排布。
- 默认具有 `0.5rem` 的内边距,除非设置了 `nonepack: true`
```css
{
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 0.5rem; /* 或 0px当 nonepack=true */
}
```
---
## 方法说明
### `constructor(opts)`
初始化按钮实例,继承自 `Layout` 并设置基本样式和属性。
#### 参数
- `opts` (Object): 配置选项对象。
#### 行为
1. 调用父类构造函数 `super(opts)`
2. 初始化默认样式对象Flex 布局相关)。
3. 根据 `nonepack` 设置是否去除 padding 和 border。
4. 根据 `orientation` 设置 `flexDirection` 并记录简写形式(`this.orient = 'h'|'v'`)。
5. 设置组件 ID 为 `opts.name`
6. 执行 `opts_setup()` 创建子组件(图标/文本)。
7. 合并默认样式与用户自定义样式到 `dom_element.style`
---
### `create()`
创建底层 DOM 元素。
```js
this.dom_element = document.createElement('button');
```
> 此方法在组件生命周期中自动调用,无需手动执行。
---
### `opts_setup()`
根据配置项创建子控件(图标和/或文本),并将它们添加到当前容器中。
#### 行为
- 如果存在 `opts.icon`,创建一个 `bricks.Icon` 实例,并绑定点击事件。
- 如果存在 `opts.label`,创建一个 `bricks.Text` 实例,启用 i18n 支持,并绑定点击事件。
- 所有子控件均通过 `add_widget()` 加入布局。
- 文本控件引用保存在 `this.text_w` 上,便于后续更新。
#### 示例代码片段
```js
if (this.opts.icon) {
const icon = new bricks.Icon({
rate: this.item_rate,
url: this.opts.icon
});
this.add_widget(icon);
icon.bind('click', this.target_clicked.bind(this));
}
```
---
### `target_clicked(event)`
处理按钮点击事件的核心方法。
#### 参数
- `event` (Event): 浏览器原生事件对象。
#### 行为
1. 输出调试日志:`bricks.debug('target_clicked() .... called ')`
2. 阻止事件冒泡(`event.stopPropagation()`
3. 触发组件自身的 `'click'` 事件,并携带完整 `opts` 数据。
4. 若配置了 `action`,且启用了 `debug` 模式,则输出完整的 `opts` 到控制台。
#### 示例输出debug 模式)
```text
debug:opts= { name: "btn_save", action: { target: "form", actiontype: "submit" }, ... }
```
---
## 事件机制
| 事件名 | 触发时机 | 携带数据 |
|----------|------------------------|--------------------|
| `click` | 用户点击按钮时 | `this.opts` 对象 |
可通过 `.bind('click', handler)` 监听此事件:
```js
button.bind('click', function(data) {
console.log('Button clicked:', data.action);
});
```
---
## 注册信息
```js
bricks.Factory.register('Button', bricks.Button);
```
使得可以通过工厂模式动态创建按钮:
```js
const btn = bricks.Factory.create('Button', {
name: 'my_button',
label: 'Submit',
icon: '/icons/save.svg',
action: {
actiontype: 'submit',
target: 'form1'
}
});
```
---
## 使用示例
```js
const myButton = new bricks.Button({
name: 'btn_ok',
label: 'OK',
icon: '/icons/check.png',
orientation: 'horizontal',
item_rate: 1.2,
color: '#fff',
bgcolor: '#007bff',
tooltip: 'Click to confirm',
action: {
actiontype: 'navigate',
target: 'page_home'
},
debug: true
});
document.body.appendChild(myButton.dom_element);
```
---
## 注意事项
- 推荐配合 `bricks.Icon``bricks.Text` 使用以保持 UI 一致性。
- 若需完全自定义外观,请使用 `css` 属性覆盖样式。
- `nonepack: true` 适合嵌入紧凑型 UI 区域。
- 所有文本自动支持国际化i18n前提是 `bricks.app.i18n` 已正确配置。
---
## 版本信息
- **框架**Bricks.js
- **组件路径**`bricks.Button`
- **最后更新**:根据代码注释推断为近期维护版本
---
✅ *文档完*

View File

@ -1,355 +0,0 @@
# `bricks.Camera` 技术文档
> 一个基于 `bricks.Popup` 的摄像头操作组件,支持拍照和视频录制功能。
---
## 概述
`bricks.Camera``bricks` UI 框架中的一个类,继承自 `bricks.Popup`,用于在网页中实现摄像头访问、实时预览、拍照以及视频录制功能。它通过调用浏览器的 `MediaDevices.getUserMedia()` 接口获取摄像头流,并结合 HTML5 `<video>``<canvas>` 实现图像捕获与处理。
该组件支持两种模式:
- **拍照模式picture**:单张照片拍摄。
- **录像模式recorder**:支持开始/停止录制 WebM 格式的视频。
同时提供切换摄像头设备、删除取消等交互按钮。
---
## 继承关系
```
bricks.Popup
bricks.Camera
```
---
## 构造函数
```js
new bricks.Camera(options)
```
### 参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|-------|------|------|--------|------|
| `opts.fps` | Number | 否 | `60` | 视频预览帧率(每秒帧数),影响 `show_picture` 调用频率 |
| `opts.type` | String | 是 | `'picture'` | 模式类型:<br>`'picture'`: 拍照模式<br>`'recorder'`: 录像模式 |
| `opts.auto_dismiss` | Boolean | 否 | `false` | 是否在操作后自动关闭弹窗(内部强制设为 `false` |
> ⚠️ 注意:`auto_dismiss` 在构造函数中被强制设置为 `false`,以防止误关闭。
---
## 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `this.recordedChunks` | Array | 存储媒体录制过程中产生的数据块Blob |
| `this.mediaRecorder` | MediaRecorder \| null | 浏览器原生 `MediaRecorder` 实例,用于视频录制 |
| `this.stream` | MediaStream \| null | 来自摄像头的媒体流对象 |
| `this.video` | HTMLVideoElement | 用于播放摄像头视频流的 `<video>` 元素 |
| `this.imgw` | bricks.Image | 显示当前画面预览的图像控件 |
| `this.shot_btn` | bricks.Svg | 拍照或开始/停止录制的按钮图标 |
| `this.cur_camera_id` | Number | 当前使用的摄像头设备索引(未实际使用,建议优化) |
| `this.task_period` | Number | 帧刷新周期(单位:秒),由 `fps` 计算得出 |
| `this.task` | TaskHandle | 使用 `schedule_once` 创建的定时任务句柄 |
| `this.dataurl` | String | 最近一帧画面的 Data URLbase64 编码的 JPEG 图像) |
| `this.record_status` | String | 录制状态:<br>`''`(初始)<br>`'standby'`(待机)<br>`'recording'`(正在录制) |
---
## 方法
### `constructor(opts)`
初始化摄像头组件,创建 UI 布局并绑定事件。
#### 内部逻辑
1. 设置默认帧率和禁用自动关闭。
2. 调用父类 `super(opts)` 初始化弹窗。
3. 初始化录制相关变量。
4. 创建 UI 结构:
- 上方填充区域显示实时画面(`this.imgw`
- 下方水平工具栏包含:
- 切换摄像头按钮
- 拍照 / 录制按钮
- 删除(关闭)按钮
5. 绑定按钮点击事件:
- `shot_btn`:根据模式执行 `take_picture``switch_recording`
- `switch_btn`:切换摄像头
- `del_btn`:关闭弹窗
6. 延迟 0.1 秒启动摄像头(`startCamera`
---
### `async startCamera(vpos?)`
启动摄像头并开始视频流预览。
#### 参数
- `vpos` (Number, 可选):指定摄像头设备索引(前置/后置)
#### 流程
1. 调用全局应用对象 `bricks.app.start_camera(vpos)` 获取摄像头流。
2. 将流赋值给 `this.stream` 并绑定到 `<video>` 元素。
3. 开始播放视频。
4. 启动周期性任务 `show_picture`,按设定帧率更新预览图。
#### 示例
```js
await this.startCamera(0); // 使用第一个摄像头
```
> ✅ 依赖全局 `bricks.app` 提供 `video_devices``video_stream`
---
### `show_picture()`
将当前视频帧渲染到预览区(通过 Canvas 截图)。
#### 步骤
1. 创建临时 `<canvas>`,尺寸匹配视频分辨率。
2. 使用 `drawImage` 将视频当前帧绘制到 canvas。
3. 转换为 JPEG Data URL质量 95%)。
4. 更新 `this.imgw` 显示新图像。
5. 调用 `schedule_once` 自身,维持帧率循环。
#### 性能提示
- 若 `this.task_period === 0`,则退出循环(如拍照后停止)。
- 使用 `requestAnimationFrame` 类似的调度机制(`schedule_once`)。
---
### `async switch_camera(btn, event)`
切换摄像头设备(例如前后摄像头)。
#### 参数
- `btn`触发按钮SVG 图标)
- `event`:点击事件对象(未使用)
#### 行为
1. 检查是否有多个摄像头(`bricks.app.video_devices.length < 2`)。
2. 若只有一个摄像头,则禁用按钮。
3. 否则,递增 `vpos``bricks.app.vpos`),循环切换。
4. 调用 `startCamera(vpos)` 重新初始化摄像头。
> 🔄 支持循环切换0 → 1 → ... → n-1 → 0
---
### `switch_recording()`
控制录像的开始与停止。
#### 行为
| 当前状态 | 动作 |
|---------|------|
| `recording` | 停止录制,按钮变“开始”图标 |
| `standby` 或空 | 开始录制,按钮变“停止”图标 |
#### 触发方法
- `videorecorder_start()`:开始录制
- `videorecorder_stop()`:停止录制
---
### `videorecorder_start()`
使用 `MediaRecorder API` 开始录制视频。
#### 异常
- 如果 `this.stream` 未初始化,抛出错误。
#### 配置
- 使用 `new MediaRecorder(stream)`
- 数据格式:`video/webm`
- 监听 `ondataavailable`:收集每个 chunk 到 `recordedChunks`
- 监听 `onstop`
- 合并所有 chunks 成一个 Blob
- 创建 File 对象:`recorded_video.webm`
- 创建 Object URL仅供调试日志
- 触发 `recorded` 事件,携带生成的 `File`
#### 示例输出
```js
dispatch('recorded', file); // file.type === "video/webm"
```
---
### `videorecorder_stop()`
手动停止媒体录制。
```js
this.mediaRecorder.stop();
```
> 自动触发 `onstop` 回调,完成文件合成与事件派发。
---
### `take_picture(event)`
执行拍照操作。
#### 参数
- `event`:点击事件,调用 `stopPropagation()` 防止冒泡
#### 行为
1. 取消当前预览任务(`task.cancel()`)。
2. 停止帧更新(`task_period = 0`)。
3. 派发 `shot` 事件,携带最近一帧的 Data URL。
#### 示例响应
```js
camera.bind('shot', function(dataurl) {
const img = document.createElement('img');
img.src = dataurl;
document.body.appendChild(img);
});
```
---
## UI 结构
```text
+----------------------------------+
| [Preview] |
| (bricks.Image: imgw) |
+----------------------------------+
| [Switch] [Action] [Delete] |
| (SVG) (SVG) (SVG) |
+----------------------------------+
```
- 使用 `bricks.HBox` 水平布局底部按钮栏
- 使用 `bricks.Filler` 占位扩展空间
- 所有按钮均为 `bricks.Svg` 组件,加载 SVG 资源图标
---
## 事件
| 事件名 | 触发时机 | 携带数据 |
|-------|----------|---------|
| `shot` | 用户拍照完成 | `dataurl`JPEG base64 字符串) |
| `recorded` | 视频录制结束 | `File` 对象WebM 格式) |
可通过 `bind()` 方法监听:
```js
const camera = new bricks.Camera({ type: 'picture' });
camera.bind('shot', (dataurl) => {
console.log('Photo taken:', dataurl);
});
camera.open();
```
---
## 图标资源SVG
| 按钮 | 图标路径 | 提示文字 |
|------|--------|--------|
| 拍照 | `imgs/camera.svg` | Take a picture |
| 开始录制 | `imgs/start_recording.svg` | Start or stop record video |
| 停止录制 | `imgs/stop_recording.svg` | Recording... |
| 切换摄像头 | `imgs/switch-camera.svg` | switch camera |
| 删除/取消 | `imgs/delete.svg` | canel it应为 cancel |
> 图标路径通过 `bricks_resource()` 解析,通常指向 `/resources/...`
---
## 依赖项
| 依赖 | 说明 |
|------|------|
| `bricks.Popup` | 父类,提供弹窗基础功能 |
| `bricks.Svg` | SVG 图标按钮 |
| `bricks.Image` | 图像显示控件 |
| `bricks.HBox`, `bricks.Filler` | 布局容器 |
| `bricks.app` | 全局应用对象,需提供:<br>- `video_devices`: 摄像头设备列表<br>- `vpos`: 当前设备索引<br>- `start_camera(vpos)`: 启动摄像头方法<br>- `video_stream`: 摄像头媒体流 |
| `schedule_once(fn, delay)` | 自定义异步调度函数(类似 `setTimeout`,但可能集成任务队列) |
| `bricks_resource(path)` | 资源路径解析函数 |
---
## 注册
```js
bricks.Factory.register('Camera', bricks.Camera);
```
允许通过工厂方式创建实例:
```js
bricks.Factory.create('Camera', { type: 'recorder' });
```
---
## 使用示例
### 拍照模式
```js
const camera = new bricks.Camera({
type: 'picture',
fps: 30
});
camera.bind('shot', (dataurl) => {
document.getElementById('result').src = dataurl;
});
camera.open();
```
### 录像模式
```js
const recorder = new bricks.Camera({
type: 'recorder',
fps: 25
});
recorder.bind('recorded', (file) => {
const url = URL.createObjectURL(file);
const a = document.createElement('a');
a.href = url;
a.download = 'video.webm';
a.click();
});
recorder.open();
```
---
## 注意事项 & 改进建议
| 问题 | 建议 |
|------|------|
| `record_status == ''` 应为 `=` | 修复拼写错误,避免逻辑 bug |
| `task.cancel()``task` 应为 `this.task` | 修复变量引用错误 |
| `tip:'canel it'` 拼写错误 | 改为 `'cancel it'` |
| `this.cur_camera_id` 未更新 | 在 `switch_camera` 中同步更新 |
| 不支持分辨率配置 | 可扩展 `opts.constraints` 传入 `getUserMedia` |
| WebM 兼容性有限 | 可检测支持 MIME 类型,降级为 MP4若支持 |
| 缺少错误处理 UI | 添加摄像头权限失败提示 |
---
## 版本信息
- **作者**:未知(来自 `bricks` 框架)
- **最后修改**:根据代码推断为现代浏览器环境开发
- **兼容性**:需支持 `MediaDevices`, `MediaRecorder`, `Canvas` 的现代浏览器Chrome/Firefox/Edge
---
📌 **总结**
`bricks.Camera` 是一个功能完整、结构清晰的前端摄像头操作组件,适用于需要拍照或短时间录像的应用场景,如头像上传、短视频录制等。配合 `bricks` 框架可快速集成至项目中。

View File

@ -1,323 +0,0 @@
# `bricks.Cols` 技术文档
> **模块**`bricks.Cols`
> **继承自**`bricks.VBox`
> **用途**:用于展示分页数据的列式布局组件,支持滚动加载、点击事件、动态渲染记录等特性。
---
## 概述
`bricks.Cols` 是一个基于 `VBox` 布局容器构建的数据展示控件,专为高效展示大量可分页数据而设计。它通过垂直滚动加载前后页内容(懒加载),并以列的形式排列每条记录,适用于移动端和桌面端的响应式布局。
该组件常用于新闻列表、商品展示、用户卡片墙等场景。
---
## 继承结构
```
bricks.Widget
└── bricks.Container
└── bricks.Box
└── bricks.VBox
└── bricks.Cols
```
---
## 配置选项Constructor Options
| 参数 | 类型 | 说明 |
|------|------|------|
| `data_url` | `String` | 获取数据的 API 地址,用于异步加载分页数据。 |
| `data_params` | `Object` | 初始请求参数对象,在每次请求时合并发送。 |
| `data_method` | `String` | 请求方法(如 `"GET"``"POST"`),默认由 `PageDataLoader` 决定。 |
| `page_rows` | `Number` | 每页显示的数据行数。 |
| `cache_limit` | `Number` | 缓存的最大页数,超出后自动清理旧页。 |
| `col_width` | `String/Number` | 单个列的宽度CSS 格式,如 `"200px"`)。 |
| `col_cwidth` | `String` | 列的弹性宽度CSS `flex` 值,如 `"1fr"`)。 |
| `mobile_cols` | `Number` | 移动端每行显示的列数,默认为 `2`。 |
| `record_view` | `Object` | 记录项的视图配置,定义如何使用 `widgetBuild` 渲染单条数据。 |
| `title` | `String` | 可选标题文本(支持国际化)。 |
| `description` | `String` | 可选描述文本(支持 Markdown。 |
| `toolbar` | `Object` | 工具栏配置对象,将被构造成 `bricks.Toolbar` 实例。 |
---
## 事件Events
| 事件名 | 触发条件 | 参数 |
|--------|----------|------|
| `record_click` | 用户点击某条记录时触发 | `(recordData)` — 当前记录的原始数据对象 |
| `command` | 工具栏发出命令时转发此事件(通过 `dispatch` | 命令名称及参数 |
> 使用 `.bind(event, handler)` 方法监听这些事件。
---
## 属性Properties
| 属性 | 类型 | 描述 |
|------|------|------|
| `loader` | `bricks.PageDataLoader` | 分页数据加载器实例,负责管理前后页数据获取与缓存。 |
| `container` | `bricks.VScrollPanel` | 外层垂直滚动容器,监听滚动阈值以触发分页加载。 |
| `main` | `bricks.DynamicColumn` | 实际存放记录项的动态列容器。 |
| `select_record` | `Widget` | 当前选中的记录对应的 widget 实例。 |
| `loading` | `Boolean` | 是否正在加载数据,防止重复请求。 |
| `title_w` | `bricks.Title4` | 标题组件实例(如果设置了 `title`)。 |
| `desc_w` | `bricks.MdWidget` | 描述组件实例(如果设置了 `description`)。 |
| `toolbar_w` | `bricks.Toolbar` | 工具栏组件实例(如果设置了 `toolbar`)。 |
---
## 方法Methods
### `constructor(opts)`
初始化 `Cols` 组件,并创建子组件结构。
#### 流程说明:
1. 调用父类构造函数。
2. 初始化 `PageDataLoader` 加载器。
3. 创建滚动容器 `VScrollPanel` 并绑定滚动边界事件。
4. 根据配置添加标题、描述、工具栏。
5. 创建主内容区域 `DynamicColumn`
6. 延迟 0.5 秒调用 `load_first_page()` 加载首屏数据。
---
### `command_handle(event)`
处理来自工具栏的命令事件,并将其转为 `dispatch` 派发出去。
```js
this.toolbar_w.bind('command', this.command_handle.bind(this));
```
---
### `async handle_click(rw, event)`
处理单个记录项的点击事件。
#### 功能:
- 阻止事件冒泡。
- 取消上一个选中项的高亮样式。
- 设置当前项为选中状态并添加 `selected_record` CSS 类。
- 打印日志并派发 `record_click` 事件,携带 `user_data`
> `rw.user_data` 存储的是该记录的原始数据对象。
---
### `async dataHandle(d)`
处理从 `loader` 返回的数据包,并渲染到界面。
#### 参数:
- `d`: 数据对象,格式如下:
```js
{
rows: [...], // 数据数组
add_page: Number, // 添加到哪一页
delete_page: Number, // (可选)需删除的页码(用于翻页替换)
pos_rate: Number // 滚动位置比例0~1
}
```
#### 行为逻辑:
- 若是向前翻页(非最大页),则反转数据顺序以便插入顶部。
- 使用 `widgetBuild(record_view, parent, data)` 动态生成每个记录 widget。
- 绑定点击事件。
- 设置 `data-page` 属性用于后续删除。
- 插入至 `main` 容器开头或末尾。
- 如有 `delete_page`,调用 `delete_page()` 清理旧页。
---
### `delete_page(page)`
删除指定页码的所有 DOM 元素及其 widget。
#### 实现方式:
- 查询所有 `[data-page="X"]` 的元素。
- 获取其关联的 `bricks_widget` 实例。
- 从 `main` 中移除对应 widget。
---
### `create_main_widget()`
重新创建主内容区域(`DynamicColumn`),通常在刷新或重置布局时调用。
```js
this.main = new bricks.DynamicColumn({
width: "100%",
col_cwidth: this.col_cwidth,
mobile_cols: this.mobile_cols || 2
});
```
---
### `async show_with_data(data)`
直接传入本地数据进行展示(绕过 `data_url` 请求)。
> ⚠️ 注意:目前代码中存在错误,应为 `await this.load_first_page(params);` 而不是 `await load_first_page(params);`
#### 正确实现建议:
```js
async show_with_data(data){
this.data = data;
this.data_url = null;
await this.load_first_page(); // 不需要参数
}
```
---
### `async load_first_page(params)`
加载第一页数据。
#### 流程:
1. 显示加载动画(`Running` 组件)。
2. 检查是否已在加载中,避免重复请求。
3. 合并 `data_params` 与传入的 `params`
4. 调用 `loader.loadData()` 获取数据。
5. 成功后清空主容器并调用 `dataHandle(d)`
6. 出错时打印调试信息。
7. 最终隐藏加载动画,释放锁。
---
### `async load_previous_page()`
向上滚动触底时加载前一页。
- 自动计算滚动位置并恢复。
- 支持 `pos_rate` 定位。
- 错误时仅输出 debug 日志。
---
### `async load_next_page()`
向下滚动触底时加载下一页。
- 类似于 `load_previous_page`,但加载下一页。
- 注释掉的 `scrollTop` 表示暂未启用精确滚动定位。
---
## 内部机制
### 分页加载策略
利用 `bricks.PageDataLoader` 实现智能分页:
- 支持双向分页(上一页 / 下一页)。
- 支持缓存控制(`cache_pages`)。
- 数据按“页”组织,可通过 `data-page` 属性追踪来源。
### 滚动加载触发
通过 `VScrollPanel` 的两个事件实现无限滚动:
| 事件 | 触发条件 |
|------|----------|
| `min_threshold` | 滚动到顶部附近 → 加载前一页 |
| `max_threshold` | 滚动到底部附近 → 加载下一页 |
绑定方式:
```js
this.container.bind('min_threshold', this.load_previous_page.bind(this));
this.container.bind('max_threshold', this.load_next_page.bind(this));
```
---
## 使用示例
### 基本用法
```js
var cols = new bricks.Cols({
title: "Latest Articles",
description: "A list of recent posts.",
data_url: "/api/articles",
data_params: { category: "tech" },
page_rows: 10,
cache_limit: 5,
col_cwidth: "1fr",
mobile_cols: 2,
record_view: {
type: "CardView",
fields: ["title", "summary", "image"]
}
});
cols.bind("record_click", function(data) {
console.log("Selected:", data);
});
document.body.appendChild(cols.dom_element);
```
### 手动加载本地数据
```js
cols.show_with_data({
rows: [
{id: 1, name: "Item 1"},
{id: 2, name: "Item 2"}
]
});
```
---
## 注意事项
1. **性能优化**:只保留可视区域附近的页面,其他页面可手动卸载。
2. **内存管理**:长时间运行应用应注意 `cache_limit` 设置,避免内存泄漏。
3. **事件绑定**:确保 `handle_click` 使用 `.bind(this, w)` 正确绑定上下文。
4. **CSS 样式**
- `.selected_record`:用于标记选中项,请在 CSS 中定义高亮样式。
- `.filler`:应用于 `container`,可能影响背景或间距。
---
## 注册信息
```js
bricks.Factory.register('Cols', bricks.Cols);
```
可在模板中通过 `<widget type="Cols" ...>` 方式声明使用。
---
## 版本信息
- **作者**Bricks Framework Team
- **最后更新**:根据代码推断为现代异步 JS 架构ES6+
- **兼容性**:需支持 Promise、async/await、Custom Elements
---
## 相关组件
| 组件 | 作用 |
|------|------|
| `bricks.PageDataLoader` | 提供分页数据加载能力 |
| `bricks.VScrollPanel` | 提供滚动检测与阈值事件 |
| `bricks.DynamicColumn` | 实现响应式多列布局 |
| `bricks.widgetBuild` | 动态构建子组件的核心函数 |
---
**文档完成度**:完整覆盖功能、接口、流程与使用方式。
🔧 **待修复问题**`show_with_data` 方法中对 `load_first_page` 的调用缺少 `this.` 上下文。

View File

@ -1,125 +0,0 @@
accordion.js:bricks.Factory.register('Accordion', bricks.Accordion);
asr.js:bricks.Factory.register('ASRClient', bricks.ASRClient);
audio.js:bricks.Factory.register('AudioPlayer', bricks.AudioPlayer);
audio.js:bricks.Factory.register('AudioRecorder', bricks.AudioRecorder);
audio.js:bricks.Factory.register('TextedAudioPlayer', bricks.TextedAudioPlayer);
bar.js:bricks.Factory.register('ChartBar', bricks.ChartBar);
bricks.js:registerfunction action:
bricks.js: 'not registered',
bricks.js: case 'registerfunction':
button.js:bricks.Factory.register('Button', bricks.Button);
camera.js:bricks.Factory.register('Camera', bricks.Camera);
cols.js:bricks.Factory.register('Cols', bricks.Cols);
conform.js:bricks.Factory.register('Conform', bricks.Conform);
continueaudio.js:bricks.Factory.register('ContinueAudioPlayer', bricks.ContinueAudioPlayer);
countdown.js:bricks.Factory.register('Countdown', bricks.Countdown);
countdown.js:bricks.Factory.register('TimePassed', bricks.TimePassed);
datagrid.js:bricks.Factory.register('DataGrid', bricks.DataGrid);
datarow.js:bricks.Factory.register('DataRow', bricks.DataRow);
dataviewer.js:bricks.Factory.register('DataViewer', bricks.DataViewer);
docxviewer.js:bricks.Factory.register('DOCXviewer', bricks.DOCXviewer);
docxviewer.js:bricks.Factory.register('EXCELviewer', bricks.EXCELviewer);
docxviewer.js:bricks.Factory.register('PDFviewer', bricks.PDFviewer);
dynamicaccordion.js:bricks.Factory.register('DynamicAccordion', bricks.DynamicAccordion);
dynamiccolumn.js:bricks.Factory.register('DynamicColumn', bricks.DynamicColumn);
factory.js: register(name, widget){
floaticonbar.js:bricks.Factory.register('IconBar', bricks.IconBar);
floaticonbar.js:bricks.Factory.register('IconTextBar', bricks.IconTextBar);
floaticonbar.js:bricks.Factory.register('FloatIconBar', bricks.FloatIconBar);
floaticonbar.js:bricks.Factory.register('FloatIconTextBar', bricks.FloatIconTextBar);
form.js:bricks.Factory.register('InlineForm', bricks.InlineForm);
form.js:bricks.Factory.register('Form', bricks.Form);
gobang.js:bricks.Factory.register('Gobang', bricks.Gobang);
html.js:bricks.Factory.register('Html', bricks.Html);
iconbarpage.js:bricks.Factory.register('IconbarPage', bricks.IconbarPage);
iframe.js:bricks.Factory.register('NewWindow', bricks.NewWindow);
iframe.js:bricks.Factory.register('Iframe', bricks.Iframe);
image.js:bricks.Factory.register('Image', bricks.Image);
image.js:bricks.Factory.register('StatedIcon', bricks.StatedIcon);
image.js:bricks.Factory.register('Icon', bricks.Icon);
image.js:bricks.Factory.register('BlankIcon', bricks.BlankIcon);
input.js: register(name, uitype,Klass){
input.js: bricks.Factory.register(name, Klass);
input.js:Input.register('UiAudio', 'audiorecorder', bricks.UiAudio);
input.js:Input.register('UiStr', 'str', bricks.UiStr);
input.js:Input.register('UiHide', 'hide', bricks.UiHide);
input.js:Input.register('UiTel', 'tel', bricks.UiTel);
input.js:Input.register('UiDate', 'date', bricks.UiDate);
input.js:Input.register('UiInt', 'int', bricks.UiInt);
input.js:Input.register('UiFloat', 'float', bricks.UiFloat);
input.js:Input.register('UiCheck', 'check', bricks.UiCheck);
input.js:Input.register('UiCheckBox', 'checkbox', bricks.UiCheckBox);
input.js:Input.register('UiEmail', 'email', bricks.UiEmail);
input.js:Input.register('UiFile', 'file', bricks.UiFile);
input.js:Input.register('UiImage', 'image', bricks.UiImage);
input.js:Input.register('UiCode', 'code', bricks.UiCode);
input.js:Input.register('UiText', 'text', bricks.UiText);
input.js:Input.register('UiPassword', 'password', bricks.UiPassword);
input.js:Input.register('UiAudio', 'audio', bricks.UiAudio);
input.js:Input.register('UiVideo', 'video', bricks.UiVideo);
input.js:Input.register('UiAudio', 'audiotext', bricks.UiAudio);
input.js:Input.register('UiSearch', 'search', bricks.UiSearch);
keypress.js:bricks.Factory.register('KeyPress', bricks.KeyPress);
layout.js:bricks.Factory.register('HBox', bricks.HBox);
layout.js:bricks.Factory.register('FHBox', bricks.FHBox);
layout.js:bricks.Factory.register('VBox', bricks.VBox);
layout.js:bricks.Factory.register('FVBox', bricks.FVBox);
layout.js:bricks.Factory.register('Filler', bricks.Filler);
layout.js:bricks.Factory.register('HFiller', bricks.Filler);
layout.js:bricks.Factory.register('VFiller', bricks.Filler);
layout.js:bricks.Factory.register('ResponsableBox', bricks.ResponsableBox);
line.js:bricks.Factory.register('ChartLine', bricks.ChartLine);
llm.js:bricks.Factory.register('LlmIO', bricks.LlmIO);
llmout.js:bricks.Factory.register('LlmOut', bricks.LlmOut);
markdown_viewer.js:bricks.Factory.register('MarkdownViewer', bricks.MarkdownViewer);
markdown_viewer.js:bricks.Factory.register('MdWidget', bricks.MdWidget);
menu.js:bricks.Factory.register('Menu', bricks.Menu);
message.js:bricks.Factory.register('Message', bricks.Message);
message.js:bricks.Factory.register('Error', bricks.Error);
miniform.js:bricks.Factory.register('MiniForm', bricks.MiniForm);
modal.js:bricks.Factory.register('Modal', bricks.Modal);
modal.js:bricks.Factory.register('ModalForm', bricks.ModalForm);
multiple_state_image.js:bricks.Factory.register('MultipleStateImage', bricks.MultipleStateImage);
period.js:bricks.Factory.register('PeriodDays', bricks.PeriodDays);
pie.js:bricks.Factory.register('ChartPie', bricks.ChartPie);
popup.js:bricks.Factory.register('Popup', bricks.Popup);
popup.js:bricks.Factory.register('PopupWindow', bricks.PopupWindow);
progressbar.js:bricks.Factory.register('ProgressBar', bricks.ProgressBar);
qaframe.js:bricks.Factory.register('QAFrame', bricks.QAFrame);
recorder.js:bricks.Factory.register('SysCamera', bricks.SysCamera);
recorder.js:bricks.Factory.register('WidgetRecorder', bricks.WidgetRecorder);
recorder.js:bricks.Factory.register('SysAudioRecorder', bricks.SysAudioRecorder);
recorder.js:bricks.Factory.register('SysVideoRecorder', bricks.SysVideoRecorder);
registerfunction.js: register(n, f){
running.js:bricks.Factory.register('Running', bricks.Running);
scroll.js:bricks.Factory.register('VScrollPanel', bricks.VScrollPanel);
scroll.js:bricks.Factory.register('HScrollPanel', bricks.HScrollPanel);
splitter.js:bricks.Factory.register('Splitter', bricks.Splitter);
streaming_audio.js:bricks.Factory.register('StreamAudio', bricks.StreamAudio);
streaming_audio.js:bricks.Factory.register('ASRText', bricks.StreamAudio);
svg.js:bricks.Factory.register('Svg', bricks.Svg);
svg.js:bricks.Factory.register('StatedSvg', bricks.StatedSvg);
svg.js:bricks.Factory.register('MultipleStateIcon', bricks.MultipleStateIcon);
tab.js:bricks.Factory.register('TabPanel', bricks.TabPanel);
tabular.js:bricks.Factory.register('Tabular', bricks.Tabular);
toolbar.js:bricks.Factory.register('Toolbar', bricks.Toolbar);
tree.js:bricks.Factory.register('Tree', bricks.Tree);
tree.js:bricks.Factory.register('EditableTree', bricks.EditableTree);
utils.js: register(n, icon){
vadtext.js:bricks.Factory.register('VadText', bricks.VadText);
videoplayer.js:bricks.Factory.register('Iptv', bricks.Iptv);
videoplayer.js:bricks.Factory.register('VideoPlayer', bricks.VideoPlayer);
videoplayer.js:bricks.Factory.register('Video', bricks.VideoPlayer);
websocket.js:bricks.Factory.register('WebSocket', bricks.WebSocket);
webspeech.js:bricks.Factory.register('WebTTS', bricks.WebTTS);
webspeech.js:bricks.Factory.register('WebASR', bricks.WebASR);
widget.js:bricks.Factory.register('Tooltip', bricks.Tooltip);
widget.js:bricks.Factory.register('Text', bricks.Text);
widget.js:bricks.Factory.register('KeyinText', bricks.KeyinText);
widget.js:bricks.Factory.register('Title1', bricks.Title1);
widget.js:bricks.Factory.register('Title2', bricks.Title2);
widget.js:bricks.Factory.register('Title3', bricks.Title3);
widget.js:bricks.Factory.register('Title4', bricks.Title4);
widget.js:bricks.Factory.register('Title5', bricks.Title5);
widget.js:bricks.Factory.register('Title6', bricks.Title6);
wterm.js:bricks.Factory.register('Wterm', bricks.Wterm);

View File

@ -1,249 +0,0 @@
# `bricks.Conform` 技术文档
> 一个基于 `bricks.PopupWindow` 的确认对话框类,用于展示消息并提供“确认”与“取消”操作选项。
---
## 概述
`bricks.Conform``bricks` UI 框架中的一个模态弹窗组件,继承自 `bricks.PopupWindow`。它被设计用于在用户执行关键操作前进行确认,例如删除数据或提交表单。该组件自动显示,并包含可自定义的确认/取消按钮,支持国际化文本和事件回调。
---
## 类定义
```javascript
class bricks.Conform extends bricks.PopupWindow
```
---
## 构造函数
### `constructor(opts)`
初始化一个 `Conform` 实例。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 配置选项对象,继承自 `PopupWindow` 并扩展以下属性: |
##### 扩展配置项 (`opts`)
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `message` | String | 必填 | 要显示的消息内容支持换行和国际化i18n。 |
| `timeout` | Number | `0` (自动覆盖为 0) | 弹窗自动关闭时间(毫秒),此组件中强制设为 `0`,即不自动关闭。 |
| `auto_open` | Boolean | `true` (自动设置) | 是否在创建后立即打开弹窗,此处固定为 `true`。 |
| `conform` | Object | 可选 | “确认”按钮的自定义配置,如标签、事件等。 |
| `discard` | Object | 可选 | “取消”按钮的自定义配置。 |
> ⚠️ 注意:构造函数内部会强制设置 `opts.timeout = 0``opts.auto_open = true`,以确保用户必须手动选择操作。
#### 示例
```javascript
var confirmDialog = new bricks.Conform({
message: "确定要删除此文件吗?",
conform: {
label: "删除",
handler: function(){ console.log("已确认"); }
},
discard: {
label: "保留"
}
});
```
---
## 方法说明
### `create_conform()`
创建主容器布局并将消息区域与工具栏添加进去。
- 使用 `VBox` 布局占满整个窗口。
- 调用 `create_message()``create_toolbar()` 分别构建内容区与操作区。
- 将整体布局通过 `add_widget()` 添加到弹窗中。
---
### `create_message(widget)`
创建消息显示区域,支持长文本滚动和居中对齐。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `widget` | `bricks.VBox` | 容器部件,用于承载消息内容。 |
#### 内部结构
1. 创建一个 `Filler` 占位符,允许内容拉伸填充。
2. 在 `Filler` 中嵌入 `VScrollPanel` 支持垂直滚动。
3. 添加 `Text` 组件显示消息:
- 启用文本换行 (`wrap: true`)
- 水平居中对齐 (`halign: 'middle'`)
- 支持国际化 (`i18n: true`)
---
### `create_toolbar(widget)`
创建底部操作工具栏,包含“确认”和“取消”两个按钮。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `widget` | `bricks.VBox` | 容器部件,用于添加工具栏。 |
#### 工具栏配置
使用 `bricks.IconTextBar` 创建图标+文字按钮栏:
| 按钮名称 | 图标 | 默认标签 | 自定义来源 |
|---------|------|----------|------------|
| `conform` | `imgs/conform.svg` | "Conform" | `this.opts.conform` |
| `discard` | `imgs/cancel.svg` | "Discard" | `this.opts.discard` |
> ✅ 所有自定义属性将通过 `bricks.extend()` 合并到默认配置中。
#### 事件绑定
- `conform` → 触发 `conform_hndl`
- `discard` → 触发 `discard_hndl`
---
### `conform_hndl(event)`
“确认”按钮点击处理函数。
#### 行为
1. 调用 `this.dismiss()` 关闭弹窗。
2. 触发名为 `'conformed'` 的自定义事件,供外部监听。
#### 示例监听
```javascript
confirmDialog.on('conformed', function(){
// 执行确认逻辑
});
```
---
### `discard_hndl(event)`
“取消”按钮点击处理函数。
#### 行为
1. 调用 `this.dismiss()` 关闭弹窗。
2. 触发名为 `'cancelled'` 的自定义事件。
#### 示例监听
```javascript
confirmDialog.on('cancelled', function(){
// 执行取消后的逻辑
});
```
---
## 事件列表
| 事件名 | 触发时机 | 携带数据 |
|-------|----------|--------|
| `conformed` | 用户点击“确认”按钮后 | 无 |
| `cancelled` | 用户点击“取消”按钮后 | 无 |
可通过 `.on(event, handler)``.bind(event, handler)` 注册监听。
---
## 注册信息
```javascript
bricks.Factory.register('Conform', bricks.Conform);
```
- 通过工厂模式注册名称为 `'Conform'` 的可实例化组件。
- 可通过 `bricks.Factory.create('Conform', options)` 动态创建实例。
---
## 布局结构图
```plaintext
PopupWindow
└── VBox (100% x 100%)
├── Filler
│ └── VScrollPanel
│ └── Text (消息内容)
└── IconTextBar
├── [✔] Conform 按钮
└── [✖] Discard 按钮
```
---
## 样式与资源依赖
- **图标资源路径**
- 确认图标:`bricks_resource('imgs/conform.svg')`
- 取消图标:`bricks_resource('imgs/cancel.svg')`
- 使用 `i18n: true`,需配合国际化语言包解析多语言文本。
---
## 使用建议
✅ 推荐场景:
- 删除、覆盖、退出等危险操作前的二次确认。
- 需要用户明确响应的操作流程中断点。
❌ 不适用场景:
- 非阻塞性提示(应使用 Toast 或 Banner
- 需长时间停留的信息展示(考虑使用普通窗口)。
---
## 完整示例
```javascript
var dialog = new bricks.Conform({
message: "您确定要退出编辑模式吗?未保存的内容将会丢失。",
conform: { label: "退出", style: "danger" },
discard: { label: "继续编辑" }
});
dialog.on('conformed', function(){
window.location.href = "/home";
});
dialog.on('cancelled', function(){
console.log("用户选择保留");
});
```
---
## 版本信息
- **框架版本**`bricks.js`
- **组件作者**:未知(基于命名空间推断)
- **最后更新**:根据代码逻辑推断为现代 ES6+ 风格实现
---
📌 *文档生成于2025年4月*

View File

@ -1,314 +0,0 @@
# `ContinueAudioPlayer` 技术文档
> 一个基于 Web Audio API 的连续音频播放器组件,支持流式播放、音量控制、暂停/恢复、静音切换和事件回调。
---
## 概述
`bricks.ContinueAudioPlayer` 是一个继承自 `bricks.VBox` 的音频播放类,专为在浏览器中实现无缝连续音频播放而设计。它使用 Web Audio API 解码并播放 Base64 编码或 ArrayBuffer 格式的音频数据,并支持动态控制播放状态与音量。
该组件适用于需要精确时间控制的语音合成TTS、实时音频流处理等场景。
---
## 类定义
```js
class ContinueAudioPlayer extends bricks.VBox
```
注册名称:`'ContinueAudioPlayer'`
注册方式:
```js
bricks.Factory.register('ContinueAudioPlayer', bricks.ContinueAudioPlayer);
```
---
## 构造函数
### `constructor(options)`
初始化播放器实例。
#### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|------|
| `options.ws_url` | `string` | WebSocket 地址(当前未实际使用,保留字段) |
| `options.onStart` | `Function` | 音频开始播放时触发的回调函数 |
| `options.onEnd` | `Function` | 当前音频片段结束播放时触发 |
| `options.onPause` | `Function` | 暂停播放时触发 |
| `options.onResume` | `Function` | 恢复播放时触发 |
| `options.onVolumeChange` | `Function` | 音量变化时触发,参数为新音量值 |
#### 示例
```js
const player = new bricks.ContinueAudioPlayer({
ws_url: 'wss://example.com/audio',
onStart: () => console.log('Audio started'),
onEnd: () => console.log('Audio ended'),
onPause: () => console.log('Paused'),
onResume: () => console.log('Resumed'),
onVolumeChange: (vol) => console.log(`Volume: ${vol}`)
});
```
---
## 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `audioContext` | `AudioContext` | Web Audio 上下文对象 |
| `gainNode` | `GainNode` | 控制音量的增益节点 |
| `nextStartTime` | `Number` | 下一段音频应开始播放的时间(以秒为单位) |
| `started` | `Boolean` | 是否已成功初始化音频上下文 |
| `muted` | `Boolean` | 当前是否处于静音状态 |
| `volume` | `Number` | 当前音量范围0.0 - 1.0 |
| `ws_url` | `String` | 存储传入的 WebSocket URL目前仅作存储用途 |
| `options` | `Object` | 用户传入的配置选项 |
---
## 方法
### `initAudioContext()`
创建并初始化 `AudioContext``GainNode`,准备音频播放环境。
- 若上下文已被关闭,则可通过调用此方法重新激活。
- 自动设置初始增益值为 `this.volume`
- 更新 `nextStartTime` 为当前时间,确保后续音频从正确时间点开始。
> ⚠️ 注意:由于浏览器策略限制,**必须在用户交互(如点击)后才能创建或恢复 AudioContext**。
---
### `base64ToArrayBuffer(base64) → ArrayBuffer`
将 Base64 字符串转换为 `ArrayBuffer`,用于 Web Audio API 解码。
#### 参数
| 参数名 | 类型 | 描述 |
|-------|------|------|
| `base64` | `string` | Base64 编码的二进制音频数据 |
#### 返回值
- `{ArrayBuffer}`:可用于 `decodeAudioData` 的原始二进制缓冲区。
#### 示例
```js
const buffer = player.base64ToArrayBuffer("UklGRiQAAABXQVZFZm...");
```
---
### `handleAudioTrack(arrayBuffer)`
解码并播放一段音频数据。
#### 参数
| 参数名 | 类型 | 描述 |
|-------|------|------|
| `arrayBuffer` | `ArrayBuffer` | 包含编码音频数据的二进制缓冲区(如 WAV、MP3 等) |
#### 行为说明
1. 使用 `audioContext.decodeAudioData()` 异步解码音频。
2. 创建 `AudioBufferSourceNode` 并连接到增益节点。
3. 在合适的时间点开始播放(避免重叠):
- 开始时间为 `Math.max(currentTime, this.nextStartTime)`
4. 更新 `nextStartTime` 为本次播放结束时间,保证下段音频接续播放。
5. 触发 `onStart` 回调。
6. 播放结束后触发 `onEnd` 回调。
#### 错误处理
若解码失败,会在控制台输出错误日志:
```text
Error decoding audio data: [Error]
```
---
### `pauseAudio()`
暂停所有正在播放的音频。
- 调用 `audioContext.suspend()` 挂起上下文。
- 成功后触发 `onPause` 回调。
> 📝 仅当 `audioContext.state === 'running'` 时有效。
---
### `resumeAudio()`
恢复被暂停的音频播放。
- 调用 `audioContext.resume()` 恢复上下文运行。
- 成功后触发 `onResume` 回调。
> 📝 仅当 `audioContext.state === 'suspended'` 时有效。
---
### `restart()`
停止当前播放,关闭音频上下文,并重新初始化,实现“重新开始”。
#### 行为流程
1. 如果上下文存在且未关闭,先调用 `close()`
2. 关闭完成后调用 `initAudioContext()` 重建上下文。
3. 重置播放时间线。
> ✅ 可用于重置播放状态或应对长时间运行后的资源释放问题。
---
### `setVolume(value)`
设置播放音量。
#### 参数
| 参数名 | 类型 | 范围 | 描述 |
|-------|------|------|------|
| `value` | `number` | `0.0 ~ 1.0` | 目标音量值 |
#### 功能
- 自动限制输入值在 `[0, 1]` 范围内。
- 更新 `this.volume`
- 如果已初始化 `gainNode`,则更新其 `gain.value`(若已静音则保持为 0
- 触发 `onVolumeChange` 事件。
#### 示例
```js
player.setVolume(0.75); // 设置音量为 75%
```
---
### `toggleMute()`
切换静音状态。
- 若当前非静音,则进入静音状态(`gain.value = 0`)。
- 若当前静音,则恢复至原音量(`gain.value = this.volume`)。
- 切换后更新 `this.muted` 并触发 `onVolumeChange`
---
### `emit(eventName, ...args)`
触发指定事件回调(如果定义了对应函数)。
#### 参数
| 参数名 | 类型 | 描述 |
|-------|------|------|
| `eventName` | `string` | 回调函数名(对应 `options[eventName]` |
| `...args` | any | 传递给回调的参数 |
#### 示例
```js
this.emit('onStart'); // 调用 options.onStart()
```
---
## 使用示例
```js
// 创建播放器实例
const audioPlayer = new bricks.ContinueAudioPlayer({
onStart: () => console.log("▶️ 开始播放"),
onEnd: () => console.log("⏹️ 播放结束"),
onVolumeChange: (v) => console.log(`🔊 音量: ${v}`)
});
// 假设你有一段 Base64 音频数据
const base64Audio = "UklGRiQAAABXQVZFZm1...";
const buffer = audioPlayer.base64ToArrayBuffer(base64Audio);
// 播放音频
audioPlayer.handleAudioTrack(buffer);
// 控制操作
audioPlayer.setVolume(0.5); // 调整音量
audioPlayer.toggleMute(); // 切换静音
audioPlayer.pauseAudio(); // 暂停
audioPlayer.resumeAudio(); // 恢复
audioPlayer.restart(); // 重启播放器
```
---
## 浏览器兼容性
| 浏览器 | 支持情况 |
|--------|----------|
| Chrome | ✅ 支持 |
| Firefox | ✅ 支持 |
| Safari | ⚠️ 需要 `webkitAudioContext` 兼容写法(已内置处理) |
| Edge | ✅ 支持 |
| iOS Safari | ✅(注意自动播放策略) |
> 🔐 **注意**:出于安全策略,大多数现代浏览器要求 **用户手势(如点击)** 后才能启动音频上下文。
---
## 注意事项
1. **自动播放限制**
大多数浏览器禁止页面加载时自动播放音频。建议在用户点击事件中初始化或调用 `handleAudioTrack`
2. **内存管理**
长时间连续播放大量音频可能导致内存占用上升,请合理管理音频数据生命周期。
3. **跨域音频数据**
所有音频数据需满足同源策略或 CORS 要求,否则无法解码。
4. **精度同步**
利用 `AudioContext.currentTime` 实现高精度播放调度,适合多段连续播放场景。
---
## 图解工作流程
```
[Base64]
↓ base64ToArrayBuffer()
[ArrayBuffer]
↓ handleAudioTrack()
decodeAudioData() → AudioBuffer
↓ createBufferSource()
[Source Node] → gainNode → destination
startTime = max(now, nextStartTime)
nextStartTime += duration
```
---
## 许可与归属
© 2025 Bricks Framework. All rights reserved.
开源协议:请参考项目 LICENSE 文件。
---
📌 **提示**:结合 WebSocket 或 Fetch 流式传输,可实现 TTS 实时语音播报系统。

View File

@ -1,284 +0,0 @@
# Bricks 时间组件技术文档
本模块提供两个基于 `bricks` 框架的时间显示类组件:`TimePassed`(计时器)和 `Countdown`(倒计时器),并包含一个通用的时间格式化工具函数。这些组件可用于构建需要时间展示功能的 UI 界面。
---
## 目录
- [1. 核心工具函数](#1-核心工具函数)
- [`bricks.formatTime(seconds)`](#bricksformattimeseconds)
- [2. TimePassed 类(正向计时器)](#2-timepassed-类正向计时器)
- [继承关系](#继承关系)
- [构造函数](#构造函数)
- [方法](#方法)
- [3. Countdown 类(倒计时器)](#3-countdown-类倒计时器)
- [继承关系](#继承关系-1)
- [配置选项 (`opts`)](#配置选项-opts)
- [事件](#事件)
- [构造函数](#构造函数-1)
- [方法](#方法-1)
- [4. 工厂注册](#4-工厂注册)
- [5. 使用示例](#5-使用示例)
---
## 1. 核心工具函数
### `bricks.formatTime(seconds)`
将秒数转换为 `HH:MM:SS` 格式的字符串,不足两位用前导零填充。
#### 参数
| 参数名 | 类型 | 描述 |
|---------|--------|--------------|
| seconds | number | 时间(以秒为单位) |
#### 返回值
- `{string}`:格式化后的时间字符串,格式为 `HH:MM:SS`
#### 示例
```js
bricks.formatTime(3661); // 输出 "01:01:01"
bricks.formatTime(59); // 输出 "00:00:59"
```
#### 实现说明
- 小时 = `Math.floor(seconds / 3600)`
- 分钟 = `Math.floor((seconds % 3600) / 60)`
- 秒 = `seconds % 60`
- 每部分使用 `.padStart(2, '0')` 补齐至两位数字。
---
## 2. TimePassed 类(正向计时器)
用于从 0 开始递增显示已过去的时间如“00:00:05”表示运行了 5 秒)。
### 继承关系
```js
class TimePassed extends bricks.VBox
```
> 继承自 `bricks.VBox`,作为容器可包含子控件。
---
### 构造函数
```js
new bricks.TimePassed(opts)
```
#### 参数
| 参数名 | 类型 | 是否必需 | 描述 |
|----------|--------|----------|--------------------------|
| opts | object | 是 | 配置对象 |
| opts.text_rate | any | 否 | 文本渲染速率或样式参数,传递给内部 Text 组件 |
> ⚠️ 注意:当前代码中存在潜在 bug —— 使用了未定义变量 `this.t``this.text_rate`,应为 `opts.text_rate` 并初始化文本内容为 `formatTime(this.seconds)`
✅ **建议修复后的构造函数片段:**
```js
this.seconds = 0;
this.text_w = new bricks.Text({
text: bricks.formatTime(this.seconds),
rate: opts.text_rate
});
this.add_widget(this.text_w);
```
---
### 方法
| 方法名 | 描述 |
|------------|--------------------------------------------------------------|
| `start()` | 启动计时器,每秒调用一次 `add_one_second()`,开始递增时间。 |
| `stop()` | 停止计时器,取消当前调度任务。 |
#### `add_one_second()`
私有方法,由 `schedule_once` 调度执行:
- 每次使 `this.seconds += 1`
- 更新显示文本
- 自动重新调度下一次调用(形成循环)
> 使用 `bind(this)` 确保上下文正确。
⚠️ 若不停止,将持续运行。
---
## 3. Countdown 类(倒计时器)
实现一个可配置的倒计时组件,在时间归零时触发事件。
### 继承关系
```js
class Countdown extends bricks.VBox
```
> 同样继承自 `VBox` 容器组件。
---
### 配置选项 (`opts`)
| 参数名 | 类型 | 是否必需 | 描述 |
|-------------|--------|----------|----------------------------------------------------------------------|
| limit_time | string | 是 | 初始倒计时时间,格式支持 `"SS"``"MM:SS"``"HH:MM:SS"` |
| text_rate | any | 否 | 传入 Text 组件的渲染参数 |
---
### 事件
| 事件名 | 触发条件 | 数据参数 |
|-----------|----------------------------------|--------|
| `timeout` | 当倒计时结束(`this.seconds < 1`)时触发 | 无 |
可通过 `this.dispatch('timeout')` 触发事件,供外部监听。
---
### 构造函数
```js
new bricks.Countdown(opts)
```
#### 功能说明
1. 解析 `opts.limit_time` 字符串为小时、分钟、秒。
2. 支持以下格式:
- `"30"` → 30 秒
- `"5:30"` → 5 分 30 秒
- `"1:30:45"` → 1 小时 30 分 45 秒
3. 转换为总秒数存储在 `this.seconds`
4. 创建并添加 `Text` 组件用于显示时间。
> ✅ 支持任意长度分割,但仅处理 1~3 段,其余情况默认按三段解析。
---
### 方法
| 方法名 | 描述 |
|-------------------|----------------------------------------------------------------------|
| `start()` | 启动倒计时,延迟 1 秒后调用 `time_down_second()` 开始递减。 |
| `time_down_second()` | 私有方法:每次减少 1 秒,更新显示;若时间为 0则触发 `timeout` 事件并终止。 |
> 使用 `schedule_once(fn, delay)` 实现定时回调,形成递归倒计时。
---
## 4. 工厂注册
为了支持动态创建组件实例,通过工厂模式注册两个类:
```js
bricks.Factory.register('Countdown', bricks.Countdown);
bricks.Factory.register('TimePassed', bricks.TimePassed);
```
### 用途
允许通过字符串名称动态创建组件,例如:
```js
let timer = bricks.Factory.create('TimePassed', { text_rate: 'high' });
let cd = bricks.Factory.create('Countdown', { limit_time: '00:01:00' });
```
---
## 5. 使用示例
### 示例 1创建并启动正向计时器
```js
let timePassed = new bricks.TimePassed({
text_rate: 'normal'
});
// 添加到页面或其他容器
someContainer.add_widget(timePassed);
// 开始计时
timePassed.start();
// 停止计时
// timePassed.stop();
```
### 示例 2创建倒计时器并监听超时事件
```js
let countdown = new bricks.Countdown({
limit_time: '00:00:10', // 10 秒倒计时
text_rate: 'large'
});
countdown.on('timeout', function() {
console.log('倒计时结束!');
alert('时间到!');
});
someContainer.add_widget(countdown);
countdown.start(); // 启动倒计时
```
### 示例 3使用工厂创建组件
```js
let widget = bricks.Factory.create('Countdown', {
limit_time: '01:30:00'
});
widget.on('timeout', () => {
console.log('倒计时完成');
});
container.add_widget(widget);
widget.start();
```
---
## 注意事项与改进建议
1. **Bug 修复建议**
- 在 `TimePassed` 构造函数中,`text: this.t` 应改为 `text: bricks.formatTime(this.seconds)`
- `rate: this.text_rate` 应为 `rate: opts.text_rate`
2. **性能优化**
- 可考虑使用 `setInterval` 替代递归 `schedule_once`,但当前方式更灵活且兼容异步调度系统。
3. **扩展性建议**
- 可增加 `pause()` 方法支持暂停/恢复。
- 提供 `getFormattedTime()` 公共方法获取当前时间字符串。
4. **输入验证**
- 对 `limit_time` 的解析可加入非法值检测(如非数字)。
---
## 版本信息
- 模块名:`bricks.time`
- 作者Bricks Framework Team
- 最后更新2025-04-05
---
📌 *本文档适用于 Bricks UI 框架 v1.x+*

View File

@ -1,88 +0,0 @@
# CSS 多列布局配置文档
本技术文档描述了一个用于定义多列布局样式的 JavaScript 对象 `css`,其中包含多列布局的样式配置。
---
## 概述
该对象 `css` 用于存储与 CSS 多列布局相关的样式属性。当前仅定义了一个名为 `multicolumns` 的子对象,用于配置多列布局的宽度和间距。
---
## 配置结构
```javascript
var css = {
multicolumns: {
columnWidth: '350px',
colummGap: '10px'
}
}
```
> ⚠️ 注意:`colummGap` 存在拼写错误,应为 `columnGap`
---
## 属性说明
### `multicolumns`
一个包含多列布局相关 CSS 属性的对象。
| 属性名 | 类型 | 默认值 | 描述 |
|---------------|--------|------------|------|
| `columnWidth` | 字符串 | `'350px'` | 定义每列的宽度。此处设置为 `350px`,表示每列宽 350 像素。 |
| `colummGap` | 字符串 | `'10px'` | ⚠️ **拼写错误**:应为 `columnGap`。用于设置列之间的间距,当前设置为 `10px`。 |
---
## 使用建议
### 修正拼写错误
建议将 `colummGap` 更正为 `columnGap`,以确保与标准 CSS 属性名称一致:
```javascript
var css = {
multicolumns: {
columnWidth: '350px',
columnGap: '10px' // 已修正拼写
}
}
```
### 实际应用示例
可将此配置动态应用于 DOM 元素,例如:
```javascript
const element = document.getElementById('content');
element.style.columnWidth = css.multicolumns.columnWidth;
element.style.columnGap = css.multicolumns.columnGap;
```
或结合 CSSOM 使用:
```css
/* 等效的 CSS 写法 */
#content {
column-width: 350px;
column-gap: 10px;
}
```
---
## 浏览器兼容性
- `column-width``column-gap` 是 CSS Multi-column Layout Module 的一部分。
- 现代浏览器普遍支持,但在旧版浏览器(如 IE中不支持。
- 推荐在使用时添加必要的厂商前缀或进行兼容性检测。
---
## 总结
该配置对象提供了一种通过 JavaScript 管理多列布局样式的简便方式。建议修复属性名拼写错误,并在生产环境中结合实际需求进行扩展和封装。

View File

@ -1,434 +0,0 @@
# `bricks.DataGrid` 技术文档
> **基于 `bricks.js` 框架的可冻结列数据表格组件**
---
## 概述
`bricks.DataGrid` 是一个功能丰富的前端数据网格组件,支持:
- 冻结列(固定左侧列)
- 虚拟滚动与分页加载
- 动态字段渲染(通过 `uitype``uioptions`
- 行选择、点击事件处理
- 工具栏、迷你表单集成
- 多语言支持i18n
该组件继承自 `bricks.VBox`,采用模块化设计,结合 `bricks.Row` 实现每行数据的封装和交互。
---
## 类结构
### 1. `bricks.Row`
表示数据网格中的一行记录。
#### 构造函数:`constructor(dg, rec)`
| 参数 | 类型 | 说明 |
|------|------|------|
| `dg` | `bricks.DataGrid` | 所属的数据网格实例 |
| `rec` | `Object` | 当前行的数据对象 |
**初始化内容:**
- 复制数据:`this.data = objcopy(rec)`
- 初始化列容器:`freeze_cols`, `normal_cols`
- 创建列控件:调用 `create_col_widgets()` 分别生成冻结列和普通列
- 设置宽度样式
- 绑定点击处理器
#### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `dg` | `DataGrid` | 所属的数据网格对象 |
| `data` | `Object` | 当前行原始数据副本 |
| `freeze_cols` | `Array<bricks.Widget>` | 冻结列中的控件列表 |
| `normal_cols` | `Array<bricks.Widget>` | 非冻结列中的控件列表 |
| `name_widgets` | `Object` | 字段名 → 控件映射表 |
| `click_handler` | `Function` | 点击事件处理器(绑定到所属 DataGrid |
| `freeze_row` | `bricks.HBox``null` | 包含所有冻结列控件的水平布局容器 |
| `normal_row` | `bricks.HBox``null` | 包含所有非冻结列控件的水平布局容器 |
#### 方法
##### `create_col_widgets(fields, cols)``bricks.HBox|null`
根据字段定义创建对应的 UI 控件,并放入指定数组中。
**参数:**
- `fields`: 字段配置数组
- `cols`: 输出控件数组(引用传递)
**逻辑流程:**
1. 遍历每个字段 `f`
2. 合并默认选项:
```js
{
name: f.name,
label: f.label,
uiype: f.uitype,
width: f.width,
required: true,
row_data: objcopy(this.data),
readonly: true
}
```
3. 根据 `uitype` 创建不同控件:
- `'button'`: 使用 `bricks.Button`,绑定 `button_click`
- 其他类型:使用 `bricks.viewFactory(opts, this.data)`
4. 为控件设置样式flex 布局、最小宽度)
5. 将控件加入 `cols` 数组及 `name_widgets` 映射
6. 最终返回一个包含这些控件的 `HBox`
**返回值:**
- 成功时返回 `HBox` 容器
- 若无字段则返回 `null`
---
##### `button_click(event)`
按钮点击回调函数。
**行为:**
- 临时重写 `getValue()` 方法,使其返回整行数据
- 准备动作描述符 `desc`(含 action、params、datawidget 等)
- 触发全局处理器 `universal_handler`
> ⚠️ 注意:此方法在 `Button` 上下文中执行(通过 `.bind(w)` 绑定)
---
##### `selected()`
将当前行设为“已选”状态(视觉高亮)。
**操作:**
- 遍历所有列控件,移除 `'selected'` CSS 类
##### `unselected()`
取消当前行的选择状态。
**操作:**
- 添加 `'selected'` CSS 类至所有列控件
##### `toogle_select(e, f)`
手动切换 DOM 元素的选中类。
| 参数 | 类型 | 说明 |
|------|------|------|
| `e` | `HTMLElement` | 要操作的元素 |
| `f` | `Boolean` | 是否添加 `'selected'` 类 |
---
### 2. `bricks.DataGrid` (主类)
继承自 `bricks.VBox`,实现完整的数据表格功能。
#### 构造函数:`constructor(opts)`
**参数 `opts` 支持以下属性:**
| 属性 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `dataurl` | `String` | 否 | - | 数据接口 URL用于异步加载 |
| `method` | `String` | 否 | `'GET'` | 请求方式 |
| `params` | `Object` | 否 | `{}` | 请求参数 |
| `title` | `String` | 否 | - | 表格标题(显示为 `Title1` |
| `description` | `String` | 否 | - | 表格描述文本 |
| `show_info` | `Boolean` | 否 | `false` | 是否显示信息栏 |
| `miniform` | `Object` | 否 | - | 查询条件迷你表单配置 |
| `toolbar` | `Object` | 否 | - | 工具栏按钮配置 |
| `row_height` | `Number/String` | 否 | `'auto'` | 表头行高度 |
| `header_css` | `String` | 否 | `'grid_header'` | 表头 CSS 类名 |
| `body_css` | `String` | 否 | `'grid_body'` | 表体 CSS 类名 |
| `fields` | `Array<Object>` | 是 | - | 字段定义数组(见下文) |
| `check` | `Boolean` | 否 | `false` | 是否显示复选框列 |
| `lineno` | `Boolean` | 否 | `false` | 是否显示序号列 |
| `admin` | `Any` | 否 | - | 管理员权限标志(预留扩展) |
#### 字段定义 (`fields`) 结构
每个字段对象包含:
| 属性 | 类型 | 说明 |
|------|------|------|
| `name` | `String` | 字段名(对应数据键) |
| `label` | `String` | 显示标签(支持 i18n |
| `datatype` | `String` | 数据类型(如 string, int, date |
| `uitype` | `String` | 控件类型text, number, button, checkbox 等) |
| `uioptions` | `Object` | 控件额外配置项 |
| `freeze` | `Boolean` | 是否冻结(固定在左侧) |
| `width` | `Number/String` | 列宽(单位 px |
> 特殊字段自动插入:
> - `_check`: 复选框列(当 `check=true`
> - `_lineno`: 序号列(当 `lineno=true`
---
#### 主要属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `loading` | `Boolean` | 是否正在加载数据 |
| `select_row` | `bricks.Row` | 当前选中行对象 |
| `dataurl`, `method`, `params` | `String/Object` | 数据请求配置 |
| `fields` | `Array` | 所有字段定义 |
| `freeze_fields` / `normal_fields` | `Array` | 分离后的冻结/非冻结字段列表 |
| `freeze_width` / `normal_width` | `Number` | 冻结区与主体区域总宽度px |
| `freeze_part`, `normal_part` | `VBox` | 左右两部分容器 |
| `freeze_header`, `normal_header` | `HBox` | 表头容器 |
| `freeze_body`, `normal_body` | `VScrollPanel` | 表体滚动容器 |
| `loader` | `BufferedDataLoader` | 缓冲式数据加载器 |
| `miniform` | `MiniForm` | 查询表单 |
| `toolbar` | `Toolbar` | 工具栏 |
---
#### 核心方法
##### `create_parts()`
初始化整个表格结构,包括:
1. 分离字段为 `freeze_fields``normal_fields`
2. 计算各区域宽度
3. 创建左右两个垂直容器(带滚动)
4. 构建表头(调用 `create_header()`
5. 设置同步滚动机制(`coscroll`
> ✅ 如果启用了 `check``lineno`,会自动插入特殊字段。
---
##### `create_header()`
创建表头单元格,使用 `bricks.Text` 显示字段 `label``name`
- 每个文本控件设置 `flex` 样式以匹配列宽
- 添加 `column_no` 属性到 DOM 元素(格式:`f0`, `n1`...),便于后续定位
---
##### `add_rows(records, direction)`
批量添加多行数据。
| 参数 | 类型 | 说明 |
|------|------|------|
| `records` | `Array<Object>` | 数据记录数组 |
| `direction` | `'up' \| 'down'` | 添加方向(默认 `'down'` |
内部循环调用 `add_row()`
---
##### `add_row(data, index)`
添加单行数据。
**步骤:**
1. 实例化 `new bricks.Row(this, data)`
2. 将其 `freeze_row``normal_row` 分别加入左右容器
3. 可指定插入位置(`index`
---
##### `clear_data()`
清空所有行数据并重置选中行。
- 清除 `freeze_body``normal_body` 中的控件
- 设置 `selected_row = null`
---
##### `del_old_rows(cnt, direction)`
删除旧的若干行(用于虚拟滚动优化)。
| 参数 | 说明 |
|------|------|
| `cnt` | 删除数量 |
| `direction` | `'up'`(顶部删除)或 `'down'`(底部删除) |
---
##### `loadData(params)`
重新加载数据(通常由外部触发)。
委托给 `this.loader.loadData(params)`
---
##### `miniform_input(event)`
监听迷你表单输入事件,自动调用 `loadData()`
---
##### `command_handle(event)`
工具栏命令处理钩子(需用户自行覆盖实现)。
---
##### `coscroll(event)`
实现左右两列区域的垂直滚动同步。
**原理:**
- 监听任一侧 `VScrollPanel``scroll` 事件
- 同步另一侧的 `scrollTop`
> 保证冻结列与主表体垂直对齐
---
##### `load_previous_data()` / `load_next_data()`
响应滚动到底部/顶部阈值时,加载前一页/后一页数据。
内部调用 `this.loader.previousPage()``nextPage()`
> 通过 `min_threshold` / `max_threshold` 事件绑定触发
---
##### `click_handler(row, event)`
行内控件点击统一处理器。
**行为:**
1. 取消之前选中行的高亮
2. 高亮当前行(调用 `row.selected()`
3. 触发 `row_click` 自定义事件
4. 输出调试日志
> 此函数作为 `bind` 上下文传入每一行控件
---
## 事件系统
| 事件名 | 触发时机 | 传递参数 |
|-------|---------|--------|
| `row_click` | 用户点击某一行 | `bricks.Row` 实例 |
| `input` (from miniform) | 迷你表单值变化 | event 对象 |
| `command` (from toolbar) | 工具栏按钮被点击 | command 名称等 |
---
## 样式类说明CSS Classes
| 类名 | 应用元素 | 用途 |
|------|----------|------|
| `datagrid` | 外层容器 | 主体样式 |
| `datagrid-grid` | 主 `HBox` | 网格布局容器 |
| `datagrid-left` | 冻结区 `VBox` | 左侧固定列容器 |
| `datagrid-right` | 主体区 `VBox` | 右侧可滚动列容器 |
| `grid_header` | 表头 `HBox` | 表头样式 |
| `datagrid-body` | `VScrollPanel` | 表体滚动区域样式 |
| `selected` | 单元格/行 | 高亮选中状态 |
---
## 注册与使用
```js
// 注册组件工厂
bricks.Factory.register('DataGrid', bricks.DataGrid);
```
### 示例用法
```js
var grid = new bricks.DataGrid({
title: "用户列表",
dataurl: "/api/users",
method: "POST",
params: { dept: "sales" },
check: true,
lineno: true,
row_height: 40,
fields: [
{ name: "name", label: "姓名", uitype: "text", freeze: true, width: 120 },
{ name: "age", label: "年龄", uitype: "int", width: 80 },
{ name: "edit", label: "编辑", uitype: "button", icon: "edit", action: { cmd: "edit_user" }, width: 60 }
],
miniform: { /* mini form config */ },
toolbar: { items: [ /* toolbar buttons */ ] }
});
container.add_widget(grid);
```
---
## 调试与日志
使用 `bricks.debug(...)` 输出关键流程日志,例如:
- 列宽计算结果
- 滚动事件触发
- 数据加载过程
可通过浏览器控制台查看。
---
## 依赖说明
本组件依赖以下 `bricks.js` 模块:
- `bricks.VBox`, `bricks.HBox`:布局容器
- `bricks.Text`, `bricks.Button`, `bricks.Title1`:基础控件
- `bricks.MiniForm`, `bricks.Toolbar`:功能控件
- `bricks.VScrollPanel`:滚动面板
- `bricks.BufferedDataLoader`:分页数据加载器
- `bricks.viewFactory`:动态视图工厂
- `objcopy()`:对象深拷贝工具
- `convert2int()`:安全转整数
- `schedule_once()`:延迟执行函数
---
## 设计亮点
**高性能虚拟滚动**
通过 `BufferedDataLoader` + `min_threshold/max_threshold` 实现大数据量流畅浏览。
**灵活 UI 扩展性**
支持任意 `uitype` 字段类型,可通过 `viewFactory` 扩展。
**双列同步滚动**
完美实现冻结列与主体列的垂直同步滚动。
**模块化架构**
`Row` 类独立封装行逻辑,易于维护和定制。
---
## 待改进点(建议)
- ❌ 错误:`self.toolbar` 应为 `this.toolbar`
- 🔧 增加 `destroy()` 方法清理事件监听
- 📈 支持横向滚动同步(如有必要)
- 💡 提供 `getValue()` 获取所有选中行数据的方法
- 🔄 支持列排序、过滤等高级功能扩展
---
## 版本信息
- 编写时间2025年4月
- 作者Bricks Framework 团队
- 文档版本v1.0
---
> 📘 提示:请确保引入完整 `bricks.js` 框架及其依赖资源CSS、图标等以正确渲染组件。

View File

@ -1,311 +0,0 @@
# `bricks.DataRow` 技术文档
> **模块**: `bricks.js`
> **类名**: `bricks.DataRow`
> **继承自**: `bricks.HBox`
---
## 概述
`bricks.DataRow` 是一个用于渲染表格行的 UI 组件通常用于数据浏览器Data Browser或列表视图中。它支持表头行和数据行两种渲染模式能够根据配置自动构建字段、工具栏并处理用户交互。
该组件继承自 `bricks.HBox`,因此具备水平布局能力,适用于构建可扩展的数据展示界面。
---
## 配置选项 (Options)
| 属性 | 类型 | 说明 |
|------|------|------|
| `toolbar` | Object / null | 工具栏配置对象,包含 `tools` 数组,定义操作图标按钮。在表头中将被替换为空白图标。 |
| `fields` | Array | 字段定义数组,每个字段描述一个列的显示方式(如名称、类型等)。 |
| `css` | String / Object | 自定义 CSS 样式,应用于整个 DataRow 容器。 |
| `browserfields` | Object | 浏览器专用配置:<br>- `exclouded`: 排除显示的字段名数组<br>- `cwidth`: 各字段的列宽映射(单位:字符宽度) |
| `editexclouded` | Array | (未使用)预留字段排除列表(当前未实现逻辑)。 |
| `header_css` | String / Object | 表头行的额外 CSS 样式。 |
| `checkField` | String | 可选字段名,用于启用复选框列(例如选择记录)。值会绑定到 `user_data[checkField]`。 |
### 字段定义 (`fields` 中的对象)
| 属性 | 类型 | 说明 |
|------|------|------|
| `name` | String | 字段名,对应数据中的键。 |
| `label` | String | 列标题;若未提供,则使用 `name` 作为标签。 |
| `uitype` | String | 控件类型(如 `'str'`, `'int'`, `'date'` 等),决定如何渲染该字段。 |
| `cwidth` | Number | 列宽(以字符为单位,默认为 10。优先级低于 `browserfields.cwidth`。 |
| `value` | Any | (仅用于表头)默认值或标签文本。 |
| 其他属性 | - | 支持传递给具体 ViewBuilder 的其他参数。 |
---
## 方法
### 构造函数 `constructor(opts)`
初始化 `DataRow` 实例并设置初始状态。
```js
new bricks.DataRow(options);
```
#### 参数:
- `opts` (Object): 配置选项对象,参见上文。
#### 内部初始化:
- 调用父类构造函数。
- 初始化 `this.record_w = null` —— 用于存放字段控件的容器。
---
### `render_header()`
渲染表头行(即列标题)。
```js
row.render_header();
```
内部调用 `this.render(true)`,指示进入“表头”模式。
---
### `render_data()`
渲染数据行(即实际记录内容)。
```js
row.render_data();
```
内部调用 `this.render(false)`,表示渲染真实数据。
---
### `render(header)`
主渲染方法,区分表头与数据行逻辑。
#### 参数:
- `header` (Boolean):是否渲染为表头。
#### 行为:
1. 若存在 `checkField`
- 表头:添加一个空图标(`bricks.BlankIcon`
- 数据行:添加一个可交互的复选框(`bricks.UiCheck`),绑定 `changed` 事件到 `get_check_state`
2. 调用 `build_fields(header)` 渲染所有字段。
---
### `renew(record)`
更新当前行的数据源并重新渲染字段内容。
#### 参数:
- `record` (Object): 新的数据记录对象。
#### 行为:
- 更新 `this.user_data = record`
- 清空原有字段容器 `record_w`
- 重建字段控件
> ⚠️ 注意:不会重建整个 DOM 结构,仅刷新字段内容,性能更优。
---
### `get_check_state(e)`
处理复选框状态变化事件。
#### 参数:
- `e` (Event): 来自 `UiCheck``changed` 事件。
#### 行为:
- 获取新值并同步回 `this.user_data[this.checkField]`
- 触发 `check_changed` 事件,携带自身实例作为参数
```js
this.dispatch('check_changed', this);
```
可用于外部监听选中状态变更。
---
### `build_toolbar(header)`
构建工具栏区域(如操作按钮:编辑、删除等)。
#### 参数:
- `header` (Boolean): 是否为表头模式。
#### 行为:
- 表头模式下,所有工具替换为占位图标(`blankicon`),保持对齐
- 数据行模式下,正常加载工具项
- 创建 `bricks.IconBar` 实例并添加至组件
- 为每个非空白工具绑定事件监听器,通过 `my_dispatch` 转发事件名
> ✅ 支持事件冒泡机制,便于外部监听工具点击事件。
---
### `my_dispatch(e)`
事件转发函数工厂,生成能正确派发事件的方法。
#### 参数:
- `e` (String): 事件名称(如 `'edit'`, `'delete'`
#### 返回:
- Function: 闭包函数,调用 `this.dispatch(e)`,确保上下文正确。
常用于绑定:
```js
w.bind(tools[i].name, this.my_dispatch(tools[i].name));
```
---
### `build_fields(header, cw?)`
创建字段容器并启动字段构建流程。
#### 参数:
- `header` (Boolean): 是否为表头
- `cw` (Widget, 可选): 外部传入的容器,默认新建 `HBox`
#### 行为:
- 创建新的 `HBox` 容器 `record_w`
- 设置样式类 `childrensize`
- 添加进主组件
- 调用 `_build_fields` 执行具体构建
---
### `_build_fields(header, cw)`
私有方法:逐个构建字段控件。
#### 参数:
- `header` (Boolean): 是否为表头
- `cw` (Container Widget): 目标容器
#### 流程:
1. 解析 `browserfields.exclouded``cwidths` 配置
2. 将 `checkField` 加入排除列表(避免重复显示)
3. 遍历 `this.fields`
- 跳过被排除的字段
- 构造字段控件参数 `opts`
- 表头:显示 `label``name`
- 数据行:从 `user_data` 提取值
- 使用 `bricks.get_ViewBuilder(uitype)` 获取对应的视图生成器
- 回退到 `'str'` 类型生成器以防不支持的类型
- 实例化控件并添加到容器
- 添加 CSS 类 `tabular-cell` 保证表格样式一致
---
## 事件系统
`DataRow` 支持以下事件分发:
| 事件名 | 触发时机 | 携带参数 |
|--------|----------|---------|
| `check_changed` | 复选框状态改变时 | 当前行实例 (`this`) |
| `[tool.name]` | 工具栏按钮被点击时(如 `'edit'`, `'delete'` | 由 `IconBar` 触发,可通过 `bind()` 监听 |
示例监听:
```js
row.bind('check_changed', function(sender){
console.log("选中状态变化:", sender.user_data);
});
```
---
## 注册信息
```js
bricks.Factory.register('DataRow', bricks.DataRow);
```
允许通过工厂方式创建实例:
```js
var row = bricks.Factory.create('DataRow', options);
```
---
## 使用示例
### 创建表头行
```js
var headerRow = new bricks.DataRow({
fields: [
{ name: 'id', label: 'ID', uitype: 'int', cwidth: 5 },
{ name: 'name', label: '姓名', uitype: 'str', cwidth: 15 },
{ name: 'age', label: '年龄', uitype: 'int' }
],
checkField: 'selected'
});
headerRow.render_header(); // 渲染列标题 + 复选框占位
container.add_widget(headerRow);
```
### 创建数据行
```js
var dataRow = new bricks.DataRow({
fields: [...],
checkField: 'selected',
browserfields: {
exclouded: ['internal_id'],
cwidths: { name: 20 }
},
toolbar: {
tools: [
{ name: 'edit', icon: 'pencil' },
{ name: 'delete', icon: 'trash' }
]
}
});
dataRow.renew({ id: 1, name: '张三', age: 25, selected: 1 });
container.add_widget(dataRow);
```
---
## 设计特点
- **双模式渲染**:同一组件支持表头与数据行,减少冗余代码。
- **灵活字段控制**:通过 `browserfields.exclouded` 动态隐藏字段。
- **列宽管理**:支持全局与字段级 `cwidth` 控制布局。
- **事件解耦**:通过 `dispatch` 实现松耦合通信。
- **可扩展性**:基于 `ViewBuilder` 插件机制支持多种字段类型。
---
## 注意事项
1. `editexclouded` 字段目前未在代码中使用,可能是遗留字段。
2. `toolbar.tools.forEach(...)` 应注意空指针风险,建议增加判空保护。
3. `_build_fields``cwidths` 键名为 `cwidths`,但配置写的是 `cwidth`,可能存在拼写错误(应统一为 `cwidth`)。
4. 建议在 `renew()` 中判断 `record_w` 是否已存在,避免重复创建。
---
## 版本信息
- **Author**: Bricks Framework Team
- **Version**: 1.0
- **Last Modified**: 2025-04-05
---
✅ 文档完

View File

@ -1,502 +0,0 @@
# `bricks.DataViewer` 技术文档
> **版本1.0**
> **继承自:`bricks.VBox`**
> **用途:通用数据展示组件,支持分页加载、滚动加载、工具栏操作与动态表单交互**
---
## 概述
`bricks.DataViewer` 是一个可扩展的数据视图组件,用于在 Web 界面中以可视化方式展示和管理结构化数据。它基于 `bricks.VBox` 容器构建,具备以下核心功能:
- 支持异步分页加载远程数据(通过 `PageDataLoader`
- 垂直滚动区域自动加载上一页/下一页
- 可配置的工具栏(支持增删改查等操作)
- 行选择与事件通知机制
- 内置编辑表单弹窗(新增、更新、克隆、删除)
- 高度可定制化(可通过子类重写关键方法实现自定义渲染)
该组件通常作为列表、表格或卡片式数据展示的基础容器使用。
---
## 组件结构
```js
var bricks = window.bricks || {};
bricks.DataViewer = class extends bricks.VBox { ... }
```
### 注册名称
```js
bricks.Factory.register('DataViewer', bricks.DataViewer);
```
可通过工厂创建:
```js
let viewer = bricks.Factory.build('DataViewer', opts);
```
---
## 构造函数
```js
constructor(opts)
```
### 参数说明
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.data_url` | String | 是 | 数据请求 URL |
| `opts.data_params` | Object | 否 | 请求附加参数 |
| `opts.page_rows` | Number | 否 | 每页行数,默认由 `PageDataLoader` 控制 |
| `opts.data_method` | String | 否 | HTTP 方法(如 `'GET'`, `'POST'`),默认 `'GET'` |
| `opts.cache_limit` | Number | 否 | 缓存页面数量限制 |
| `opts.editable` | Object | 否 | 编辑配置对象(见下文) |
| `opts.toolbar` | Object | 否 | 工具栏自定义项 |
| `opts.row_options` | Object | 否 | 行级选项(字段、排除字段等) |
### 初始化行为
1. 设置默认布局样式:
- 宽高为 `100%`
- 溢出隐藏(`overflow: hidden`
2. 创建 `PageDataLoader` 实例用于数据加载
3. 初始化状态变量(选中行、加载锁、偏移量等)
4. 绑定事件:`row_check_changed`
5. 延迟调用 `build_all()` 进行 UI 构建
---
## 核心属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `loader` | `PageDataLoader` | 负责数据分页加载 |
| `scrollpanel` | `VScrollPanel` | 主内容滚动容器 |
| `filler_widget` | `Filler` | 占位容器,容纳 `scrollpanel` |
| `toolbar_w` | `IconTextBar` | 工具栏组件 |
| `select_row` | Widget | 当前选中的记录行 widget |
| `active_item` | Widget | 当前激活项目(保留字段) |
| `loading` | Boolean | 是否正在加载数据 |
| `data_offset` | Number | 数据起始偏移位置(用于反向插入) |
| `old_params` | Object | 上次请求参数,防止重复加载 |
| `key_select_items` | Array | 支持键盘导航的选择项集合 |
| `check_changed_row` | Object | 最近一次变更的行数据 |
| `keyselectable` | Boolean | 是否允许键盘选择 |
---
## 生命周期方法
### `async build_all()`
主 UI 构建入口,按顺序执行以下步骤:
```js
await this.build_other();
this.scrollpanel.bind('min_threshold', this.load_previous_page.bind(this));
this.scrollpanel.bind('max_threshold', this.load_next_page.bind(this));
await this.render();
this.set_key_select_items();
```
#### 子构建方法:
| 方法 | 功能 |
|------|------|
| `build_title_widget()` | (预留)构建标题区 |
| `build_description_widget()` | (预留)构建描述区 |
| `build_toolbar_widget()` | 构建顶部工具栏 |
| `build_records_area()` | 创建滚动面板用于显示数据行 |
| `build_other()` | 子类可扩展的额外构建逻辑(空实现) |
---
### `async render(params)`
重新加载并渲染数据。
#### 参数
- `params`: 请求参数(合并到原始 `data_params`
#### 流程
1. 若参数未变化 → 返回
2. 使用 `loader.loadData(params)` 获取数据
3. 清空当前内容
4. 执行前置处理 `before_data_handle()`
5. 处理数据 `dataHandle(d)`
> ⚠️ 自动去重:若 `params === old_params` 则跳过。
---
### `async before_data_handle()`
钩子函数,在数据处理前调用。可用于预处理或状态清理。
> 默认为空,供子类覆盖。
---
### `async dataHandle(d)`
处理从 `loader` 返回的数据对象。
#### 输入格式示例
```json
{
"rows": [...],
"add_page": 1,
"delete_page": 2
}
```
#### 行为
- 调用 `renderPageData(rows, add_page)`
- 如果有 `delete_page`,调用 `delete_page(page_num)` 删除旧页
---
## 数据渲染相关
### `build_records_area()`
创建主数据显示区域:
```js
this.filler_widget = new bricks.Filler({});
this.add_widget(this.filler_widget);
this.scrollpanel = new bricks.VScrollPanel({});
this.filler_widget.add_widget(this.scrollpanel);
```
> 使用 `Filler + VScrollPanel` 结构确保布局适应。
---
### `async renderPageData(data, page)`
将一批数据渲染成可视行。
#### 参数
- `data`: 数组,每项是一个数据记录
- `page`: 页面编号
#### 特殊逻辑
- 如果不是最大页(历史页):数据逆序,并从前部插入(维护时间顺序)
- 否则:正常追加至末尾
内部循环调用 `build_row()`
---
### `async build_record_view(record)`
**抽象方法**:生成单条记录的 UI 组件。
#### 默认实现
```js
var w = new bricks.VBox({width: '100px', height:'100px'});
w.set_css('test_box');
return w;
```
> ✅ 必须由子类重写以实现具体展示样式(如表格行、卡片等)
---
### `async build_row(record, page, pos)`
将一条记录添加到 `scrollpanel` 中。
#### 参数
- `record`: 数据对象
- `page`: 所属页码
- `pos`: 插入位置null 表示末尾)
#### 步骤
1. 调用 `build_record_view(record)` 创建 widget
2. 设置属性 `data-page` 便于后续删除
3. 添加到 `scrollpanel` 指定位置
---
## 工具栏与用户交互
### `build_toolbar_widget()`
根据配置生成工具栏按钮。
#### 支持的操作(当 `editable` 存在时)
| 名称 | 图标 | 条件 | 提示 |
|------|------|------|------|
| `add` | `add_icon` 或默认图标 | 总是显示 | 新增记录 |
| `update` | `update_icon` | 需选中行 | 更新选中项 |
| `clone` | `clone_icon` | 需选中行 | 克隆选中项 |
| `delete` | `delete_icon` | 需选中行 | 删除选中项 |
> 图标路径通过 `bricks_resource()` 解析 SVG 资源。
此外,支持合并外部传入的 `toolbar.tools`
最终创建 `IconTextBar` 并绑定命令事件。
---
### `command_event_handle(event)`
处理工具栏点击事件。
#### 分发逻辑
| 命令名 | 行为 |
|--------|------|
| `add` | 调用 `add_record()` |
| `update` | 调用 `update_record(select_row)` |
| `clone` | 调用 `clone_record(select_row)` |
| `delete` | 调用 `delete_record(select_row)` |
| 其他 | 触发全局事件 `dispatch(name, data)` |
> 若操作需要选中行但无选中项,则提示错误。
---
## 编辑功能
### 字段控制
#### `get_edit_fields()`
提取可编辑字段,过滤掉 `editexclouded` 中指定的字段。
> 结果保存在 `this.fields` 数组中。
#### `get_hidefields()`
获取应隐藏提交的字段(来自 `data_params`),转换为 `{name, value, uitype: 'hide'}` 形式。
---
### 表单构建
| 方法 | 功能 |
|------|------|
| `build_add_form()` | 构建“新增”表单 |
| `build_update_form(data)` | 构建“更新”表单(带 id 隐藏域) |
| `build_clone_form(data)` | 构建“克隆”表单(不包含 id |
所有表单均基于 `bricks.Form`,并注入隐藏字段和编辑字段。
---
### 弹窗管理
#### `build_window(icon, title, form)`
创建通用弹窗(`PopupWindow`)封装表单。
##### 配置
- 居中定位 (`archor: "cc"`)
- 可移动、可缩放
- 尺寸:宽 90%,高 70%
- 绑定表单的 `cancel` 事件关闭窗口
---
### 编辑流程
#### `add_record()`
1. 创建新增表单
2. 弹出窗口
3. 监听 `submited` 事件 → 调用 `add_record_finish(win, event)`
#### `add_record_finish(f, event)`
1. 关闭窗口
2. 重新加载数据
3. 解析响应 JSON 并构建反馈组件(如消息提示)
#### `update_record()`
1. 获取当前选中行数据
2. 创建更新表单(含 `id` 隐藏域)
3. 弹窗并监听 `submited``update_record_finish()`
#### `update_record_finish(win, form, event)`
1. 调用 `renew_record_view(form, row)` 更新本地视图
2. 显示服务器返回结果组件
3. 关闭窗口
#### `clone_record()`
`add_record`,但初始值为原记录数据
#### `delete_record(row, record)`
弹出确认对话框(`Conform`),确认后调用 `delete_record_act()`
#### `delete_record_act()`
1. 发送 DELETE 请求POST with body
2. 接收响应并构建反馈组件
3. 若成功(返回 Message 类型),移除对应行并刷新
---
## 滚动加载机制
### `load_previous_page()`
加载前一页数据(向上滚动触底)
#### 流程
1. 检查是否已在加载 → 防抖
2. 显示 loading 指示器(`Running`
3. 调用 `loader.loadPreviousPage()`
4. 成功则调用 `dataHandle(d)`
5. 恢复滚动位置(按 `pos_rate`
6. 隐藏 loading
> 错误被捕获并打印 debug 日志。
---
### `load_next_page()`
加载下一页数据(向下滚动触底)
逻辑类似 `load_previous_page()`,但无需调整滚动位置。
---
## 辅助方法
### `set_key_select_items()`
设置支持键盘导航的元素集合(除去第一个 filler widget
用于后续方向键选择。
---
### `delete_page(page)`
批量删除属于某一页的所有 DOM 元素。
通过 `[data-page="X"]` 查询 selector 获取 widgets 并逐个移除。
---
### `record_check_changed(event)`
处理行内复选框变更事件。
转发事件为 `row_check_changed`,携带 `user_data`
---
### `renew_record_view(form, row)`
用表单最新值更新某行的 `user_data`
```js
row.user_data = { ...row.user_data, ...form._getValue() };
row.renew(data); // 视图刷新
```
> `renew()` 是 widget 的生命周期方法,需子类实现。
---
## 事件系统
| 事件名 | 触发时机 | 参数 |
|-------|---------|------|
| `row_check_changed` | 行内复选框改变 | `{user_data}` |
| `command` | 工具栏按钮点击 | `{name, selected_row}` |
| `submited` | 表单提交成功 | `{params: Response}` |
| `conformed` / `discard` | 删除确认框选择 | —— |
---
## 设计原则与扩展建议
### 可扩展点(推荐子类覆盖)
| 方法 | 用途 |
|------|------|
| `build_other()` | 添加自定义组件 |
| `build_title_widget()` | 自定义标题 |
| `build_record_view(record)` | 自定义行渲染模板 |
| `before_data_handle()` | 数据加载前准备 |
| `renew(record)` in row widget | 行内容更新逻辑 |
---
### 性能优化特性
- 数据缓存与懒加载
- 滚动阈值触发分页
- 请求去重(参数比对)
- 页面级删除释放内存
---
## 使用示例(伪代码)
```js
let viewer = new bricks.DataViewer({
data_url: '/api/users',
data_params: { dept_id: 101 },
page_rows: 20,
editable: {
add_icon: 'imgs/user_add.svg',
new_data_url: '/api/users/create',
update_data_url: '/api/users/update',
delete_data_url: '/api/users/delete'
},
row_options: {
fields: [
{ name: 'name', label: '姓名', uitype: 'text' },
{ name: 'age', label: '年龄', uitype: 'number' }
],
editexclouded: ['created_at']
}
});
// 自定义行渲染
viewer.build_record_view = function(record) {
let w = new bricks.HBox({ width: '100%', padding: 10 });
w.add_widget(new bricks.Label({ text: record.name }));
w.add_widget(new bricks.Label({ text: record.age }));
w.user_data = record;
return w;
};
```
---
## 调试信息
- `bricks.debug_obj = this.scrollpanel;` —— 方便调试滚动容器
- 所有关键操作均有 `bricks.debug()` 输出
- 支持 `bricks.show_error()` 提示用户错误
---
## 依赖组件
| 组件 | 作用 |
|------|------|
| `bricks.VBox` | 布局基类 |
| `bricks.VScrollPanel` | 滚动容器 |
| `bricks.PageDataLoader` | 分页数据加载器 |
| `bricks.IconTextBar` | 工具栏 |
| `bricks.Form` | 表单引擎 |
| `bricks.PopupWindow` | 弹窗容器 |
| `bricks.Conform` | 确认对话框 |
| `bricks.Running` | 加载指示器 |
| `bricks.HttpJson` | JSON 请求客户端 |
| `bricks.widgetBuild` | 动态组件构建 |
---
## 版本历史
| 版本 | 修改内容 |
|------|----------|
| 1.0 | 初始公开文档版本 |
---
> 📝 文档生成时间2025-04-05
> © 2025 Bricks Framework Team

View File

@ -1,31 +0,0 @@
# 创建控件的Json文件格式说明
Bricks在服务器端使用Json文件格式存储控件描述文件前端获得json文件后转化为json对象并用此json对象调用widgetBuild函数创建Bricks控件。
控件描述json文件必须含有“widgettype” 和”options“两个属性。“subwidgets”属性用来定义此控件包含的子控件。“binds”用于定义此控件或其子控件的事件处理
## id
id是一个可选项定义控件的id 缺省不定义控件id bricks提供根据控件id查找控件的机制详情请看[控件id](widgetid.md)
## widgettype说明
widgettype是一个字符串属性。其值为Bricks中的所有控件类型或"urlwidget"
可用的控件类型可以在[控件类型清单](widgets.md)中查找
## options
对象类型每个控件有特定的options属性清参看每个控件的说明
## subwidgets
数组类型数组中的每个元素必须是一个对象类型数据与desc作用一样。
参见widgetBuild函数的desc说明
## binds
列表属性,定义控件的事件处理,在列表中的每一项,定义一个事件处理, Bricks支持5种事件处理方法
分别是urlwidget, method, script, registedfunction和event
在binds中这五种事件处理方法都可以定义在同一个控件中可以灵活的使用不同事件处理方法来响应不同控件的不同事件
支持:
* 可定义bings所在控件的事件处理
* 可定义binds所在控件的子控件的事件处理
* 可定义应用控件树上任何wid对应的控件的事件处理
* 同一个控件的同一个事件,使用多个处理方法按定义顺序依次处理
详细事件处理请参看[bricks的事件处理](event.md)

View File

@ -1,30 +0,0 @@
# bricks开发和测试
## 环境准备
### 后台服务器安装部署
与bricks框架同时开发的[ahserver](https://git.opencomputing.cn/yumoqing/ahserver)的后台服务器是一个python开发的后台应用服务器。
请按照ahserver服务器的安装说明部署后台应用服务器
或者部署其他后台服务器,后台服务器需满足[对后台服务器要求](server.md)
### 将bricks的目标文件复制或链接到后台服务器的web文档根目录下
### index.ui
请参照/bricks/header.tmpl和/bricks/footer.tmpl内容编写项目的index.html文件
将项目的root控件放在index.ui中
### 用户界面编写
以".ui"后缀的文件命名编写控件描述文件。文件内容请参看[控件描述文件](descjson.md)
并保存在后台服务器的web文件目录下
### 界面调试
用控件文件的url直接调试界面

View File

@ -1,262 +0,0 @@
# Bricks 文档查看器组件技术文档
本技术文档介绍了基于 `bricks.js` 框架的三种文档文件在线预览组件:`DOCXviewer``EXCELviewer``PDFviewer`。这些组件支持通过 URL 加载并渲染 `.docx``.xlsx/.xls``.pdf` 文件,适用于网页端轻量级文档展示场景。
---
## 🔧 依赖说明
以下第三方库必须在页面中提前加载:
```html
<!-- 必需Mammoth.js - 用于 DOCX 转 HTML -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.4.2/mammoth.browser.min.js"></script>
<!-- 必需SheetJS (xlsx) - 用于 Excel 解析 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<!-- 必需PDF.js - 用于 PDF 渲染 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
```
> ⚠️ 注意:确保 `pdfjsLib` 已全局可用(通常由 PDF.js 提供)。
---
## 📦 组件概览
| 组件名 | 功能描述 | 支持格式 |
|----------------|------------------------------|--------------|
| `DOCXviewer` | 预览 Word 文档 (.docx) | `.docx` |
| `EXCELviewer` | 预览 Excel 表格文件 | `.xlsx`, `.xls` |
| `PDFviewer` | 预览 PDF 文档 | `.pdf` |
所有组件均继承自 `bricks.VBox`,可通过 `bricks.Factory.register()` 注册使用。
---
## 1. ✅ DOCXviewer
`.docx` 文件转换为 HTML 并嵌入页面显示。
### 类定义
```javascript
bricks.DOCXviewer = class extends bricks.VBox
```
### 构造函数
```javascript
constructor(opts)
```
#### 参数
- `opts` `{Object}`: 配置选项对象。
- `url` `{String}`: 指向 `.docx` 文件的远程或本地 URL。
#### 示例
```javascript
const docViewer = new bricks.DOCXviewer({
url: 'https://example.com/document.docx'
});
```
### 方法
#### `async set_url(url)`
从指定 URL 获取 `.docx` 文件,并使用 Mammoth.js 转换为 HTML 显示。
##### 流程
1. 使用 `HttpArrayBuffer` 异步获取文件二进制数据。
2. 调用 `mammoth.convertToHtml()` 将 ArrayBuffer 转为 HTML 字符串。
3. 插入到组件 DOM 容器内。
##### 参数
- `url` `{String}`: 可选,若未传则使用实例的 `this.url`
---
## 2. ✅ EXCELviewer
支持多工作表 Excel 文件的交互式浏览。
### 类定义
```javascript
bricks.EXCELviewer = class extends bricks.VBox
```
### 构造函数
```javascript
constructor(opts)
```
#### 参数
- `opts` `{Object}`:
- `url` `{String}`: Excel 文件地址。
- (自动设置)`height: "100%"`
#### UI 结构
- 上部:横向滚动条式工作表标签栏 (`HBox`)
- 下部:内容区域 (`Filler`) 显示当前选中的 sheet
#### 示例
```javascript
const excelViewer = new bricks.EXCELviewer({
url: 'https://example.com/data.xlsx'
});
```
### 方法
#### `async set_url(url)`
加载 Excel 文件并解析所有工作表名称,生成可点击标签。
##### 步骤
1. 获取文件 ArrayBuffer。
2. 使用 `XLSX.read(data, {type: 'array'})` 解析 workbook。
3. 遍历 `SheetNames` 创建文本按钮,绑定点击事件。
#### `show_sheet_by_name(sheetname, widget)`
切换显示指定工作表的内容。
##### 功能
- 更新选中状态样式(添加 `selected` CSS 类)
- 使用 `XLSX.utils.sheet_to_html()` 生成表格 HTML
- 嵌入 `VScrollPanel` 实现垂直滚动
- 替换内容容器中的子组件
##### 内部变量
- `this.cur_sheetname`: 当前显示的工作表名
- `this.workbook`: 缓存 workbook 对象以供后续访问
---
## 3. ✅ PDFviewer
逐页渲染 PDF 文件为 Canvas 图像。
### 类定义
```javascript
bricks.PDFviewer = class extends bricks.VBox
```
### 构造函数
```javascript
constructor(opts)
```
#### 参数
- `opts` `{Object}`:
- `url` `{String}`: PDF 文件路径
- (自动设置)`width: '100%'`
#### 示例
```javascript
const pdfViewer = new bricks.PDFviewer({
url: 'https://example.com/report.pdf'
});
```
### 方法
#### `async set_url(url)`
加载 PDF 并启动渲染流程。
##### 步骤
1. 使用 `HttpArrayBuffer` 获取二进制数据。
2. 调用 `pdfjsLib.getDocument({ data: ab })` 初始化文档。
3. 遍历每一页,调用 `getPage(i)` 并异步渲染。
#### `add_page_content(page)`
将单个 PDF 页面渲染为 `<canvas>` 元素并插入组件。
##### 渲染参数
- 缩放比例:`scale = 1.5`
- 使用 `page.getViewport()` 计算视口尺寸
- 创建 canvas设置宽高调用 `page.render()` 绘制
##### UI 布局
- 每页后插入一个 `Splitter` 分隔符(逻辑待修复)
- 使用 `JsWidget` 包裹 canvas 实现集成
> ❗注意:原代码中 `i < this.pdf.numPages``i` 未定义,应改为索引判断或移除条件。
##### 修复建议:
```js
// 修改 add_page_content 中的判断逻辑
if (page.pageNumber < this.pdf.numPages) {
const splitter = new bricks.Splitter();
this.add_widget(splitter);
}
```
---
## 🔗 工具函数
### `extractBodyContent(htmlString)`
提取 HTML 字符串中 `<body>...</body>` 标签之间的内容。
#### 参数
- `htmlString` `{String}`: 完整 HTML 字符串
#### 返回值
- `{String|null}`: 匹配到的 body 内容,否则返回 `null`
#### 正则表达式
```js
/<body[^>]*>([\s\S]*?)<\/body>/i
```
> 💡 当前代码中该函数被注释掉,可用于清理 XLSX 输出中的多余头尾信息。
---
## 🧱 组件注册
最后,三个组件通过 `bricks.Factory` 注册,可在模板或动态创建时使用字符串标识符:
```javascript
bricks.Factory.register('DOCXviewer', bricks.DOCXviewer);
bricks.Factory.register('EXCELviewer', bricks.EXCELviewer);
bricks.Factory.register('PDFviewer', bricks.PDFviewer);
```
### 使用示例(工厂模式)
```javascript
const viewer = bricks.Factory.create('PDFviewer', {
url: 'manual.pdf'
});
parentContainer.add_widget(viewer);
```
---
## 🛠️ 已知问题与改进建议
| 问题 | 描述 | 建议 |
|------|------|-------|
| `i` 未定义 | `add_page_content``i < numPages` 报错 | 改用 `page.pageNumber` 判断 |
| 性能问题 | 多页 PDF 同时请求可能导致卡顿 | 添加顺序加载或懒加载机制 |
| 样式控制弱 | 输出 HTML 缺乏定制化样式 | 提供 `customStyle` 回调或封装容器类 |
| 错误处理不足 | `pdfjsLib` catch 仅打印日志 | 提供错误 UI 反馈机制 |
---
## 📎 总结
这三个组件构成了一个轻量级、模块化的文档预览系统,适合集成于 Web 应用前端,实现无需下载即可查看常见办公文档的功能。
### 特性亮点
- 基于标准浏览器 API 与流行解析库
- 支持异步加载和动态渲染
- 组件化设计,易于扩展和复用
- 与 `bricks.js` UI 框架无缝集成
### 推荐用途
- 在线帮助手册预览
- 后台管理系统文档查看
- 教学平台课件展示
---
> 📚 **提示**请确保服务器允许跨域访问文档资源CORS否则 `HttpArrayBuffer.get()` 请求将失败。

View File

@ -1,399 +0,0 @@
# `DynamicAccordion` 技术文档
> **模块名称:** `bricks.DynamicAccordion`
> **继承自:** `bricks.VScrollPanel`
> **用途:** 动态可折叠列表组件,支持分页加载、内容动态渲染、编辑操作(增删改)、工具栏扩展等高级功能。
---
## 目录
- [1. 概述](#1-概述)
- [2. 核心类结构](#2-核心类结构)
- [2.1 `bricks.AccordionItem`](#21-bricksaccordionitem)
- [2.2 `bricks.AccordionInfo`](#22-bricksaccordioninfo)
- [2.3 `bricks.DynamicAccordion`](#23-bricksdynamicaccordion)
- [3. 配置选项 (`opts`)](#3-配置选项-opts)
- [4. 生命周期与构建流程](#4-生命周期与构建流程)
- [5. 主要方法说明](#5-主要方法说明)
- [5.1 渲染控制](#51-渲染控制)
- [5.2 内容生成](#52-内容生成)
- [5.3 数据处理与分页](#53-数据处理与分页)
- [5.4 编辑功能](#54-编辑功能)
- [5.5 事件系统](#55-事件系统)
- [6. CSS 类名约定](#6-css-类名约定)
- [7. 使用示例](#7-使用示例)
- [8. 注意事项](#8-注意事项)
---
## 1. 概述
`bricks.DynamicAccordion` 是一个基于 `VScrollPanel` 的高级可折叠面板组件,用于展示大量条目数据。每个条目由标题区域和可展开的内容区组成,支持:
- 分页异步加载数据
- 可配置的记录视图模板(`record_view`
- 展开后动态加载详细内容(`content_view`
- 行内工具栏(支持自定义按钮及编辑操作)
- 增删改查CRUD交互支持
- 条件性内容显示(依赖字段值)
适用于需要高效展示并操作大量结构化数据的场景,如后台管理界面中的日志、订单、用户列表等。
---
## 2. 核心类结构
### 2.1 `bricks.AccordionItem`
```js
class extends bricks.VBox
```
表示单个可折叠项的容器,包含两个子部件:
- `AccordionInfo`:点击触发展开/收起的头部
- `VBox`:隐藏的内容区域(默认不显示)
#### 构造函数
```js
constructor(opts) {
super(opts);
this.set_css('accordion-item');
}
```
#### 特性
- 自动添加 CSS 类 `accordion-item`
- 继承 `VBox` 布局特性(垂直排列)
---
### 2.2 `bricks.AccordionInfo`
```js
class extends bricks.FHBox
```
代表折叠项的标题部分,通常用于显示摘要信息,并绑定点击事件以切换内容可见性。
#### 构造函数
```js
constructor(opts) {
super(opts);
this.set_css('accordion-item-info');
}
```
#### 特性
- 使用 `FHBox` 实现水平布局
- 添加 CSS 类 `accordion-item-info`
- 存储关联的数据记录在 `user_data` 属性中
---
### 2.3 `bricks.DynamicAccordion`
主控件类负责整体逻辑调度、数据加载、UI 构建与交互响应。
```js
class extends bricks.VScrollPanel
```
#### 注册方式
```js
bricks.Factory.register('DynamicAccordion', bricks.DynamicAccordion);
```
允许通过工厂方法创建实例:
```js
bricks.createWidget("DynamicAccordion", {...});
```
---
## 3. 配置选项 (`opts`)
| 参数 | 类型 | 说明 |
|------|------|------|
| `data_url` | String | 获取数据的 API 地址 |
| `data_method` | String (可选) | 请求方法,默认为 `'GET'` |
| `data_params` | Object (可选) | 发送请求时附加的参数 |
| `cache_limit` | Number (可选) | 缓存页数上限 |
| `page_rows` | Number (可选) | 每页返回的行数 |
| `row_cheight` | Number (可选, 默认: `1.5`) | 每行高度倍率(影响布局计算) |
| `record_view` | Widget 描述对象 | 定义每行摘要区域的 UI 结构 |
| `content_rely_on` | String (可选) | 控制是否展开内容的字段名 |
| `content_rely_value` | Any (可选) | 上述字段应匹配的值才能展开内容 |
| `editable` | Object (可选) | 启用编辑模式的相关配置 |
| &nbsp;&nbsp;`.add_icon`, `.update_icon`, `.delete_icon` | String | 图标路径或资源引用 |
| &nbsp;&nbsp;`.form_cheight` | Number | 表单高度系数 |
| &nbsp;&nbsp;`.new_data_url`, `.update_data_url`, `.delete_data_url` | String | 对应操作的提交 URL |
| `fields` | Array\<Field\> | 字段定义数组,用于表单和列头渲染 |
| `record_toolbar` | Object (可选) | 自定义行工具栏按钮配置 |
| `record_toolbar_collapsable` | Boolean (可选) | 工具栏是否可折叠(未完全实现) |
| `header` | Object (可选) | 头部固定行配置(暂未启用) |
| `content_view` | Widget 描述对象 | 展开后显示的详情内容模板 |
| `title` | String (可选) | 页面标题文本i18n 支持) |
| `description` | String (可选) | 页面描述文本 |
> ⚠️ 所有字符串文本均支持国际化(`i18n: true`)。
---
## 4. 生命周期与构建流程
`build_all()` 方法是初始化入口,在构造完成后延迟执行(`schedule_once(..., 0.1)`
```js
async build_all() {
if (this.title) this.build_title();
if (this.description) this.build_description();
await this.build_toolbar();
await this.build_header();
this.container = new bricks.VScrollPanel({});
this.add_widget(new bricks.Filler().add_widget(this.container));
this.container.bind('min_threshold', this.load_previous_page.bind(this));
this.container.bind('max_threshold', this.load_next_page.bind(this));
await this.render();
}
```
### 流程图解:
```
[初始化]
build_title() → 添加标题
build_description() → 添加描述
build_toolbar() → 添加全局工具栏
build_header() → 创建表头(模拟)
创建内部容器 container (VScrollPanel)
绑定滚动触底/触顶事件 → 分页加载
render() → 加载并渲染第一页数据
```
---
## 5. 主要方法说明
### 5.1 渲染控制
#### `render(params)`
根据参数重新加载数据并刷新 UI。
- 若参数未变,则跳过
- 调用 `loader.loadData(params)` 获取数据
- 成功后调用 `dataHandle(d)`
#### `dataHandle(d)`
处理服务器返回的数据包:
```js
{
rows: [...], // 当前页数据
add_page: 2, // 新增页码
delete_page: 1 // 应删除的旧页码(超出缓存限制)
}
```
调用 `renderAccordionItems(data, page)` 渲染新项,并清理过期页。
---
### 5.2 内容生成
#### `build_item(record)`
构建单个可折叠项:
- 创建 `AccordionItem`
- 调用 `build_info(item, record)` 构建标题区
- 创建隐藏的 `content` 区域
- 绑定点击事件:`line_clicked(info, content, record)`
- 返回完整 `item`
`record === null`,则作为“表头”使用。
#### `build_info(item, record)`
生成标题区域 UI
- 使用 `widgetBuild(this.record_view, info, record)` 渲染摘要内容
- 插入 `record_toolbar`(含编辑图标)
- 设置 `info.user_data = record`
- 返回 `AccordionInfo` 实例
当无 `record` 时,用于生成列头标签。
---
### 5.3 数据处理与分页
#### `load_previous_page() / load_next_page()`
滚动接近边界时自动加载前后页:
- 显示加载动画(`Running` 提示)
- 设置 `this.loading = true` 防止重复请求
- 调用 `loader.loadPreviousPage()``loadNextPage()`
- 成功后调用 `dataHandle()` 并恢复滚动位置
- 错误捕获并输出调试日志
#### `delete_page(page)`
移除指定页的所有 DOM 元素:
```js
querySelectorAll('[data-page="X"]')
→ 移除对应 widget
```
确保内存和 DOM 不泄漏。
---
### 5.4 编辑功能
#### 新增记录
- `add_record(info)`:打开内嵌表单
- `add_record_abort()`:取消新增
- `add_record_finish()`:提交成功后刷新列表
#### 更新记录
- `update_record(info, record)`:弹出编辑表单
- `update_cancel()`:取消编辑
- `update_record_finish()`:保存后更新视图或提示
#### 删除记录
- `delete_record(info, record)`:弹出确认框
- `delete_record_act()`:发送删除请求,删除成功则移除该项
#### 新建表单快捷入口
- `build_new_form()`:顶部添加“+”按钮和隐藏表单
- 点击按钮展开/收起新建表单
- 提交后自动调用 `render()` 刷新列表
---
### 5.5 事件系统
#### 内部事件绑定
- `info.bind('click', line_clicked)`:行点击展开内容
- `IconBar` 工具按钮绑定各种事件(`add`, `update`, `delete`
- 自定义工具按钮通过 `fire_event(name, data)` 派发事件
#### 外部可监听事件
| 事件名 | 触发时机 | 参数 |
|--------|----------|------|
| `row_selected` | 用户点击某一行 | `info` widget |
| `conformed` | 删除确认后 | —— |
| `submited` | 表单提交成功 | 响应对象 |
| `[custom_tool_name]` | 自定义工具按钮被点击 | `record` 数据 |
可通过 `bind(event, handler)` 监听。
---
## 6. CSS 类名约定
| 类名 | 作用 |
|------|------|
| `.accordion-item` | 整个折叠项外层容器 |
| `.accordion-item-info` | 折叠项标题区域 |
| `.accordion-item-info-selected` | 当前行被选中状态 |
| `.accordion-item-content` | 内容区域样式基类 |
| `.filler` | 占位填充容器(用于自适应高度) |
建议配合 SCSS 定义主题风格,例如圆角、阴影、过渡动画等。
---
## 7. 使用示例
```js
var accordion = new bricks.DynamicAccordion({
title: "User List",
description: "Manage all registered users.",
data_url: "/api/users",
page_rows: 10,
cache_limit: 5,
fields: [
{ name: "name", label: "Name" },
{ name: "email", label: "Email" },
{ name: "status", label: "Status" }
],
record_view: {
widgettype: "HBox",
options: { cheight: 1.5 },
subwidgets: [
{ widgettype: "Text", options: { otext: "{{name}}" } },
{ widgettype: "Text", options: { otext: "{{email}}", halign: "right" } }
]
},
content_view: {
widgettype: "Form",
options: {
fields: [
{ name: "name", uitype: "text" },
{ name: "email", uitype: "email" },
{ name: "bio", uitype: "textarea" }
]
}
},
editable: {
form_cheight: 8,
new_data_url: "/api/users/create",
update_data_url: "/api/users/update",
delete_data_url: "/api/users/delete",
add_icon: "/static/icons/add.svg"
},
record_toolbar: {
tools: [
{ name: "view_logs", tip: "View operation logs", icon: "/icons/logs.svg" }
]
}
});
// 监听自定义操作
accordion.bind("view_logs", function(record){
console.log("查看日志:", record);
});
```
---
## 8. 注意事项
1. **性能优化**
- 支持分页缓存,避免一次性加载过多数据
- 滚动加载机制减少初始等待时间
- `cache_limit` 应合理设置以防内存占用过高
2. **内容懒加载**
- `content_view` 仅在点击展开时构建,节省资源
- 再次点击会清除内容(`clear_widgets()`),防止状态残留
3. **国际化支持**
- 所有文本建议使用 `otext` + `i18n: true` 配合语言包
4. **DOM 清理**
- 删除页面时主动移除 widgets 和事件监听(依赖框架 GC
5. **扩展性**
- 可通过 `record_toolbar` 添加自定义操作按钮
- `fire_event()` 支持插件式开发
6. **兼容性**
- 依赖 `bricks.PageDataLoader`, `bricks.HttpJson`, `bricks.widgetBuild` 等基础模块
- 需确保这些模块已正确加载
---
✅ **推荐搭配组件:**
- `bricks.Form`:用于编辑表单
- `bricks.Conform`:删除确认对话框
- `bricks.Running`:加载指示器
- `bricks.IconBar`:工具栏按钮组
📦 此组件适合集成于管理系统、数据看板等复杂前端应用中。

View File

@ -1,173 +0,0 @@
# `DynamicColumn` 组件技术文档
> **模块路径**: `bricks.DynamicColumn`
> **继承自**: `bricks.Layout`
> **用途**: 一个响应式网格布局组件,根据屏幕尺寸动态调整列数和列宽,适用于桌面与移动端。
---
## 概述
`DynamicColumn` 是 Bricks UI 框架中的一个布局类,用于创建基于 CSS Grid 的**动态列布局**。它能够根据设备类型(移动端或桌面端)、字符单位(`charsize`)以及配置参数自动计算并设置网格的列宽与间距,实现响应式设计。
该组件特别适合用于卡片列表、内容流等需要自适应列数的场景。
---
## 构造函数
```js
new bricks.DynamicColumn(options)
```
### 参数
| 参数名 | 类型 | 必填 | 默认值 | 描述 |
|----------------|----------|------|--------|------|
| `col_cwidth` | Number | 否 | 根据 `col_width` 推导 | 列宽度(以字符单位 `charsize` 为基准。例如20 表示 20 个字符宽度。 |
| `col_width` | Number | 否 | — | 固定列宽度(像素值)。若未提供 `col_cwidth`,则使用此值。 |
| `col_cgap` | Number | 否 | `0.5` | 列之间的间隙(以 `charsize` 为单位)。 |
| `mobile_cols` | Number | 否 | `1` | 在移动设备竖屏模式下强制显示的列数。 |
> ⚠️ 注意:
> - 若未传入 `col_cwidth` 且未传入 `col_width`,则默认 `col_cwidth = 20`
> - 所有参数均可通过构造选项对象传入。
### 示例
```js
const layout = new bricks.DynamicColumn({
col_cwidth: 25, // 每列宽 25ch
col_cgap: 0.8, // 列间距 0.8ch
mobile_cols: 1 // 移动端强制单列
});
```
---
## 方法说明
### `set_column_width()`
重新计算并设置当前容器的 `gridTemplateColumns``gap` 样式属性,实现动态列宽适配。
#### 逻辑流程
1. 获取当前应用的字符大小 `bricks.app.charsize`
2. 计算列间间隙:`gap = charsize × col_cgap`
3. 获取当前视口宽高:`screenWidth()``screenHeight()`
4. 判断是否为移动端竖屏:
- 如果是,则使用 `mobile_cols` 固定列数,并均分可用宽度;
- 否则:
- 优先使用 `col_cwidth` × `charsize` 作为最小列宽;
- 其次回退到 `col_width` 像素值。
5. 设置 CSS Grid 属性:
```css
grid-template-columns: repeat(auto-fill, minmax(cw + "px", 1fr));
gap: gap + "px";
```
#### 触发时机
该方法会在以下事件中被自动调用:
- 组件绑定到父元素时 (`on_parent`)
- 窗口尺寸变化时 (`resize`)
- 字符单位发生变化时 (`charsize` 事件,前提是实例具有 `cwidth` 属性)
---
## 样式行为
| CSS 属性 | 值 | 说明 |
|---------|-----|------|
| `display` | `grid` | 强制启用 CSS Grid 布局 |
| `grid-template-columns` | `repeat(auto-fill, minmax(cw, 1fr))` | 自动填充列,每列至少 `cw` 宽度,最多扩展至 1fr |
| `gap` | 动态计算的像素值 | 列与行之间的间距(由 `col_cgap` × `charsize` 决定) |
> ✅ 提示:使用 `auto-fill` 能确保在空间足够时自动添加新列,无需手动管理断点。
---
## 响应式策略
| 设备环境 | 判断条件 | 行为 |
|----------------|----------------------------------|------|
| 移动端竖屏 | `bricks.is_mobile() && width < height` | 使用 `mobile_cols` 固定列数,宽度均分 |
| 桌面/横屏设备 | 其他情况 | 使用 `col_cwidth``col_width` 作为最小列宽,启用 `minmax` 自动换行 |
---
## 注册信息
```js
bricks.Factory.register('DynamicColumn', bricks.DynamicColumn);
```
可通过工厂方式创建实例:
```js
bricks.Factory.create('DynamicColumn', { ...options });
```
---
## 使用建议
### 推荐搭配
- 配合 `bricks.Container` 或其他 `Layout` 子类作为子项使用。
- 结合 `charsize` 变化监听,实现字体缩放下的布局同步更新。
### 性能提示
- 避免频繁触发 `set_column_width`,组件已对 `resize``charsize` 做了事件绑定优化。
- 如需禁用字符单位监听,可避免设置 `this.cwidth` 属性。
---
## 示例代码
```js
// 创建一个动态列布局,每列约 20 字符宽,间隔 1ch
const dynamicCol = new bricks.DynamicColumn({
col_cwidth: 20,
col_cgap: 1,
mobile_cols: 1
});
// 添加一些内容
['Item 1', 'Item 2', 'Item 3'].forEach(text => {
const item = new bricks.Label({ text });
dynamicCol.append(item);
});
// 插入 DOM
document.body.appendChild(dynamicCol.dom_element);
```
---
## 浏览器兼容性
✅ 支持所有现代浏览器Chrome, Firefox, Safari, Edge
⚠️ 需要支持 CSS Grid Layout Level 1
---
## 版本历史
| 版本 | 修改内容 |
|------|----------|
| 1.0 | 初始实现,支持字符单位驱动的动态列布局 |
---
## 相关类
- [`bricks.Layout`](./Layout.md) - 基础布局类
- [`bricks.app`](../core/app.md) - 应用上下文,提供 `charsize`, `screenWidth()` 等工具
- [`bricks.is_mobile()`](../utils/is_mobile.md) - 设备检测工具函数
---
📝 **维护者**: Bricks UI Team
📅 **最后更新**: 2025-04-05

View File

@ -1,301 +0,0 @@
# `EchartsExt` 技术文档
> **模块路径**: `bricks.EchartsExt`
> **继承自**: `bricks.VBox`
> **依赖库**: [Apache ECharts](https://echarts.apache.org/)(需全局引入)
---
## 概述
`bricks.EchartsExt` 是一个基于 `ECharts` 的可扩展图表组件,封装在 `bricks.js` 前端框架中。它继承自 `VBox` 容器类,支持动态数据加载、响应式布局以及点击事件处理,适用于展示多维数据的可视化图表(如柱状图、折线图、饼图等)。
该组件支持从本地数据或远程 URL 加载数据,并能自动将“长格式”数据透视为“宽格式”,便于多系列图表渲染。
---
## 初始化选项Constructor Options
```js
new bricks.EchartsExt({
title: String, // 图表标题
description: String, // 图表描述(可选)
data_url: String, // 远程数据接口地址
data_params: Object, // 请求参数(附加到 data_url
method: String, // HTTP 方法,默认 'GET'
user_data: Array, // 本地数据数组(优先级高于 data_url
nameField: String, // 分组字段名(如:类别名称),默认 'name'
valueField: String, // 数值字段名(单系列时使用),默认 'value'
serieField: String, // 系列字段名(用于区分多个数据系列)
valueFields: Array, // 多数值字段数组(若未设置,则根据 serieField 自动提取)
pie_options: Object, // 特定于饼图的配置项(可选)
idField: String // 唯一标识字段,默认 'id'
})
```
> ⚠️ 注意:`user_data``data_url` 二选一。若两者都存在,优先使用 `user_data`
---
## 属性说明
| 属性 | 类型 | 描述 |
|------|------|------|
| `idField` | String | 数据对象唯一标识字段,默认 `'id'` |
| `nameField` | String | 作为 X 轴或分类维度的字段,默认 `'name'` |
| `valueField` | String | 主数值字段,默认 `'value'`(仅单系列有效) |
| `serieField` | String | 区分不同数据系列的字段(例如:“类型”、“年份”) |
| `valueFields` | Array | 所有需要显示的数据系列名称列表(自动从 `serieField` 提取) |
| `user_data` | Array | 当前加载的数据集(原始格式) |
| `data_url` | String | 数据请求地址 |
| `data_params` | Object | 额外请求参数 |
| `holder` | `bricks.Filler` | 内部占位容器,用于承载 ECharts 实例 |
| `chart` | `echarts.ECharts` | ECharts 实例对象 |
| `method` | String | HTTP 请求方法,默认 `'GET'` |
---
## 方法详解
### `constructor(opts)`
初始化图表组件,执行以下操作:
- 设置默认字段;
- 创建标题和描述组件;
- 初始化内部容器 `holder`
- 初始化 ECharts 实例;
- 若存在 `user_data` 则立即渲染;否则尝试通过 `data_url` 异步加载数据;
- 绑定窗口/元素重绘事件以触发图表 resize。
```js
this.bind('element_resize', this.chart.resize.bind(this.chart));
```
---
### `pivotify(data) → Array`
将“长格式”数据转换为“宽格式”数据,便于多系列图表展示。
#### 输入数据示例(长格式):
```json
[
{ "name": "A", "type": "销量", "value": 100 },
{ "name": "A", "type": "利润", "value": 30 },
{ "name": "B", "type": "销量", "value": 150 }
]
```
#### 输出结果(宽格式):
```json
[
{ "name": "A", "销量": 100, "利润": 30 },
{ "name": "B", "销量": 150 }
]
```
#### 处理逻辑:
1. 提取所有唯一的 `serieField` 值作为新字段;
2. 按 `nameField` 排序;
3. 构建以 `nameField` 为键的对象字典;
4. 将每个记录映射到对应行并填充相应系列值;
5. 返回对象值数组并排序。
> ✅ 使用场景:当 `serieField` 存在时,自动启用透视功能。
---
### `get_series(data) → Array`
提取数据中所有唯一的系列名称(基于 `this.serieField`)。
#### 示例:
```js
data = [
{ type: 'sales', value: 100 },
{ type: 'profit', value: 20 },
{ type: 'sales', value: 80 }
]
// get_series(data) → ['sales', 'profit']
```
返回值用于生成图例legend或系列配置。
---
### `render_data()`
根据当前 `user_data` 渲染图表。
#### 步骤:
1. 若存在 `serieField`,则调用 `pivotify()` 转换数据;
2. 初始化新的 ECharts 实例(防止内存泄漏);
3. 调用 `setup_options(data)` 获取图表配置;
4. 补充 `grid` 布局配置;
5. 若有多系列且存在 `legend`,更新其 `data` 字段;
6. 应用配置到 ECharts 实例;
7. 绑定点击事件处理器 `click_handle`
> 🔁 支持热更新:每次调用此方法都会重新绘制图表。
---
### `click_handle(params)`
ECharts 点击事件回调函数。
#### 参数:
- `params`: ECharts 返回的点击事件参数对象
#### 动作:
- 打印调试信息至控制台;
- 触发自定义事件 `'element_click'`,携带原始数据项:
```js
this.dispatch('element_click', this.user_data[params.dataIndex]);
```
> 🎯 可用于外部监听数据项点击行为,实现联动交互。
---
### `async render_urldata(params = {})`
异步从远程 URL 加载数据并渲染图表。
#### 流程:
1. 合并默认参数 `this.data_params` 与传入参数;
2. 使用 `bricks.HttpJson` 发起 HTTP 请求;
3. 成功后保存响应数据至 `this.user_data`
4. 调用 `render_data()` 渲染图表。
#### 示例请求:
```js
await jc.httpcall(this.data_url, {
method: this.method || 'GET',
params: _params
});
```
> ⏱️ 默认延迟 0.1 秒调度,避免构造期间 DOM 未就绪。
---
## 事件Events
| 事件名 | 触发时机 | 携带数据 |
|--------|----------|---------|
| `element_click` | 用户点击图表中的某个数据点 | 对应的原始数据项(来自 `user_data` |
| `element_resize` | 元素尺寸变化时(由父容器触发) | —— |
可通过 `bind()` 监听这些事件:
```js
chart.bind('element_click', function(dataItem) {
console.log('Clicked:', dataItem);
});
```
---
## 数据结构要求
### 本地数据(`user_data`)格式
```json
[
{
"name": "类别A",
"value": 120,
"type": "线上"
},
{
"name": "类别A",
"value": 80,
"type": "线下"
},
...
]
```
- 必须是对象数组;
- 至少包含 `nameField``valueField` 字段;
- 若使用多系列,需提供 `serieField` 字段。
---
## 样式与布局
- 图表容器自动适应父元素大小;
- `grid` 配置留白 3%,确保标签不被裁剪;
- 支持响应式设计,绑定 `element_resize` 事件自动调整图表尺寸。
---
## 使用示例
### 示例 1使用本地数据创建多系列柱状图
```js
var chart = new bricks.EchartsExt({
title: "销售统计",
description: "各区域线上线下销售额对比",
user_data: [
{ name: "北京", type: "线上", value: 200 },
{ name: "北京", type: "线下", value: 150 },
{ name: "上海", type: "线上", value: 180 },
{ name: "上海", type: "线下", value: 170 }
],
nameField: "name",
valueField: "value",
serieField: "type"
});
chart.bind('element_click', function(item) {
alert("点击了:" + item.name);
});
```
### 示例 2从 API 加载数据
```js
var chart = new bricks.EchartsExt({
title: "实时订单量",
data_url: "/api/orders",
data_params: { period: "today" },
nameField: "hour",
valueField: "count",
method: "GET"
});
```
---
## 注意事项
1. **ECharts 必须已全局加载**,否则 `echarts.init` 会报错;
2. `setup_options(data)` 方法需子类实现,用于定义具体图表类型(如 bar、line、pie
3. 若未定义 `serieField`,则不会进行数据透视;
4. `valueFields` 若手动指定,则忽略自动提取逻辑;
5. 建议对大数据集做分页或聚合处理,避免性能问题。
---
## 调试提示
- 查看浏览器控制台输出 `opts=` 可检查最终传递给 ECharts 的配置;
- 确保 `this.holder.dom_element` 已正确挂载且非空;
- 检查网络请求是否成功返回合法 JSON 数据。
---
## 版本信息
- **Bricks Framework**: v1.x+
- **ECharts 支持版本**: ≥ 5.0
- **兼容性**: 支持现代浏览器Chrome / Firefox / Edge / Safari
---
> 💡 提示:建议结合 `bricks.Layout` 系统使用,实现复杂仪表盘布局。

View File

@ -1,508 +0,0 @@
# bricks的事件处理
bricks的事件处理是在控件描述文件的binds区域中添加事件处理说明来实现的
## bricks支持的事件处理类型
* urlwidget
* method
* script
* registerfunction
* event
例外还支持一种混合型
* actions
在事件处理定义中使用"actiontype"属性来定义事件处理类型
## 事件处理定义数据要素
所有事件处理类型都有的数据要素有
### wid
事件发起方id关于wid规则请查看[控件id](widgetid.md)
### event
支持控件的html原生事件以及控件类中定义的事件或者是event事件处理类型中dispatch_event属性中定义的事件
### actiontype
指定事件处理类型支持“urlwidget", "method", "script", "registerfunction", "event" 或者“actions”混合型
### conform
对象类型确认控件的options如存在则此绑定需要用户确认后再执行
### datawidget
给事件添加动态参数时定义获取动态参数的控件的id关于datawidget规则请查看[控件id](widgetid.md)
如果不需要使用动态参数那么datawidget, datamethod, datascript, dataparams这几个属性可以不设置。
### datamethod
获取动态参数的方法
### datascript
获取动态参数的脚本
### dataparams
获取动态参数时需给定的参数, 可选对象类型k,v形式给定参数
### popup_options
当target为“Popup“或“PopupWindow”时定义Popup或PopupWindow的参数
#### dismiss_events
字符串数组每个字符串定义一个Popup, PopupWindow的子控件的事件这些事件发生时将导致Popup, PopupWindow关闭
#### eventpos
Popup, PopupWindow窗体移动到鼠标位置
### 获取动态参数说明
绑定任务获取实时数据作为参数,需要给定以下属性:
* datawidget字符串或控件类型获取实时数据的控件
* datamethod字符串类型控件的方法使用params作为参数调用
获取实时数据的方法
* datascript字符串类型 js脚本使用return返回数据
* dataparams参数
datamethod 优先datascript从datawidget控件中通过datamethod
### target
支持一下形式的target定义
* 目标控件实例对象
* 字符串且等于“Popup“
* 字符串且等于”PopupWindow“
* 其他字符串控件对象的id
当actiontype为"urlwidget"时target应该是一个容器控件所生成的控件将插入或替代到“target”所代表的对象中如果actiontype是其他类型,则在此对象中执行方法,脚本,定义函数,或定义事件
### conform
如果一个事件处理需要用户确认可以在事件处理中指定一个conform属性来引发当此事件发生时会弹出一个确认窗口用户确认后才会处理此事件否则不处理
不同的事件处理方法也有部分不同的事件处理属性,一下分别说明:
### urlwidget方法
urlwidget事件处理方法是从后台获取一个控件描述文件动态生成bricks控件并将控件添加添加添加或替换到事件处理指定的控件中。
urlwidget绑定需要一个options属性和一个mode属性在此属性中需要
* url字符串类型 获取desc数据的url
* mehtod字符串类型url调用的方法缺省”GET“
* params对象类型调用的参数, 从datawidget获取的数据影响此属性
绑定创建的控件添加到target控件中
例子
```
{
"widgettype":"VBox",
"options":{
"width":"100%",
"height":"100%"
},
"subwidgets":[
{
"widgettype":"HBox",
"options":{
"height":"40px"
},
"subwidgets":[
{
"id":"replace",
"widgettype":"Button",
"options":{
"width":"80px",
"i18n":true,
"label":"replace mode"
}
},
{
"id":"insert",
"widgettype":"Button",
"options":{
"width":"80px",
"i18n":true,
"label":"insert mode"
}
},
{
"id":"append",
"widgettype":"Button",
"options":{
"width":"80px",
"i18n":true,
"label":"append mode"
}
}
]
},
{
"id":"main",
"widgettype":"Filler"
}
],
"binds":[
{
"wid":"replace",
"event":"click",
"actiontype":"urlwidget",
"target":"main",
"options":{
"url":"{{entire_url('replace_text.ui')}}",
"params":{
"text":"Insert before"
}
}
},
{
"wid":"insert",
"event":"click",
"actiontype":"urlwidget",
"target":"main",
"options":{
"url":"{{entire_url('insert_text.ui')}}",
"params":{
"text":"Insert before"
}
},
"mode":"insert"
},
{
"wid":"append",
"event":"click",
"actiontype":"urlwidget",
"target":"main",
"options":{
"url":"{{entire_url('subtext.ui')}}",
"params":{
"text":"Append After"
}
},
"mode":"append"
}
]
}
```
在上述例子中我们使用了一个竖向排列的容器VBox并在此容器中添加了两个字控件分别是一个横向排列的容器HBox和一个填充器Filler
并在横向排列的子控件中添加了3个按钮控件, 每个Button定义了id 分别是replaceinsert和append在主控件VBox的binds中分别定义了三个事件处理分别对应于3个Button的click事件演示了三种子控件在target控件中插入的模式替换所有子控件插入在已有子控件之前添加到已有子控件之后
### method方法
需要指定target参数和method参数
* target类型为字符串或控件类型
如果是字符串使用“getWidgetById”函数获取控件实例。
* method字符串target实例的方法
* params传递给方法的参数
method绑定方法将事件绑定到target控件的一个方法并用params传递参数
例子
```
{
"widgettype":"VBox",
"options":{
"width":"100%",
"height":"100%"
},
"subwidgets":[
{
"widgettype":"HBox",
"options":{
"height":"40px"
},
"subwidgets":[
{
"id":"changetext",
"widgettype":"Button",
"options":{
"dynsize":false,
"width":"80px",
"i18n":true,
"label":"Change text"
}
},
{
"id":"smaller",
"widgettype":"Button",
"options":{
"dynsize":false,
"width":"80px",
"i18n":true,
"label":"Small size"
}
},
{
"id":"larger",
"widgettype":"Button",
"options":{
"dynsize":false,
"width":"80px",
"i18n":true,
"label":"larger size"
}
}
]
},
{
"widgettype":"Filler",
"options":{},
"subwidgets":[
{
"id":"text_1",
"widgettype":"Text",
"options":{
"dynsize":true,
"text":"original text"
}
}
]
}
],
"binds":[
{
"wid":"changetext",
"event":"click",
"actiontype":"method",
"target":"text_1",
"params":"new text",
"method":"set_text"
},
{
"wid":"smaller",
"event":"click",
"actiontype":"method",
"target":"app",
"method":"textsize_smaller"
},
{
"wid":"larger",
"event":"click",
"actiontype":"method",
"target":"app",
"method":"textsize_bigger"
}
]
}
```
上述例子中三个Button分别驱动app中textsize_smaller()textsize_bigger()来改变bricks字符大小从而影响到text_1控件的显示大小
### script方法
绑定脚本,此方法将事件绑定到一个脚本,支持以下属性
* script字符串脚本正文
* params对象类型脚本可以访问params变量来获取参数。
脚本中可以使用以下变量:
* this 事件处理中target对应的控件实例
* params参数对象datawidget获得的数据会添加到params中
例子
```
{
"id":"insert",
"widgettype":"Button",
"options":{
"width":"80px",
"i18n":true,
"label":"click me"
},
"binds":[
{
"wid":"self",
"event":"click",
"actiontype":"script",
"target":"self",
"script":"console.log(this, params, event);"
}
]
}
```
在上述例子中定义了使用“script”事件处理方法来处理Button的“click事件” 在click后在控制台上把事件传过来的参数显示出来
### registerfunction方法
事件绑定一个注册函数, 参看[RegisterFunction](registerfunction.md)
支持以下属性:
* rfname字符串已注册的函数名称
* params对象类型调用注册函数时作为参数传递给注册函数。
```
<link rel="stylesheet" href="http://localhost:80/bricks/css/bricks.css">
<!-- support IE8 (for Video.js versions prior to v7) -->
<script src="http://localhost:80/bricks/3parties/videojs-ie8.min.js"></script>
</head>
<body>
<script src="http://localhost:80/bricks/3parties/marked.min.js"></script>
<script src="http://localhost:80/bricks/3parties/xterm.js"></script>
<script src="http://localhost:80/bricks/3parties/video.min.js"></script>
<script src="http://localhost:80/bricks/3parties/recorder.wav.min.js"></script>
<!--- <script src="http://localhost:80/bricks/3parties/videojs-contrib-hls.min.js"></script> --->
<script src="http://localhost:80/bricks/bricks.js"></script>
<script>
/*
if (bricks.is_mobile()){
alert('wh=' + window.innerWidth + ':' + window.innerHeight);
}
*/
var myfunc = function(params){
this.set_text(params.text);
}
bricks.RF.register('setText', myfunc);
const opts = {
"widget": {
"widgettype":"VBox",
"options":{
"width":"100%",
"height":"100%"
},
"subwidgets":[
{
"widgettype":"HBox",
"options":{
"height":"40px"
},
"subwidgets":[
{
"id":"changetext",
"widgettype":"Button",
"options":{
"dynsize":false,
"width":"80px",
"i18n":true,
"label":"Change text"
}
},
{
"id":"smaller",
"widgettype":"Button",
"options":{
"dynsize":false,
"width":"80px",
"i18n":true,
"label":"Small size"
}
},
{
"id":"larger",
"widgettype":"Button",
"options":{
"dynsize":false,
"width":"80px",
"i18n":true,
"label":"larger size"
}
}
]
},
{
"widgettype":"Filler",
"options":{},
"subwidgets":[
{
"id":"text_1",
"widgettype":"Text",
"options":{
"dynsize":true,
"text":"original text"
}
}
]
}
],
"binds":[
{
"wid":"changetext",
"event":"click",
"actiontype":"registerfunction",
"target":"text_1",
"params":{
"text":"new text"
},
"rfname":"setText"
},
{
"wid":"smaller",
"event":"click",
"actiontype":"method",
"target":"app",
"method":"textsize_smaller"
},
{
"wid":"larger",
"event":"click",
"actiontype":"method",
"target":"app",
"method":"textsize_bigger"
}
]
} };
const app = new bricks.App(opts);
app.run();
</script>
</body>
</html>
```
在上述例子中使用bricks.RF注册了一个setText函数 并在主控件的binds区域定义了当changetext按钮点击后调用注册函数“setText”来处理
### event方法
绑定事件需指定target触发target对象的一个事件
支持以下属性
dispatch_event需触发的事件名
params传递给事件的参数处理函数可以使用evemt.params获得此参数
```
{
"widgettype":"VBox",
"options":{},
"subwidgets":[
{
"id":"btn1",
"widgettype":"Button",
"options":{
"label":"press me"
}
},
{
"id":"txt1",
"widgettype":"Text",
"options":{
"otext":"I will dispatch a event when btn1 pressed",
"i18n":true
}
}
],
"binds":[
{
"wid":"btn1",
"event":"click",
"actiontype":"event",
"target":"txt1",
"dispatch_event":"gpc"
},
{
"wid":"txt1",
"event":"gpc",
"actiontype":"script",
"target":"self",
"script":"alert('yield');"
}
]
}
```
上述例子中在主控件的binds区域定义了btn1的按钮点击后使用event处理类型来处理btn1的click事件即转而触发“txt1”正文控件的“gpc”事件并定义了当”txt1“正文控件在gpc事件发生后使用script事件处理方法alert一个消息
## 定义需要确认的事件处理
有时候我们需要客户确认后在处理事件,如果用户不确认,则放弃处理事件,比如在删除数据时我们通常需要用户确认一下
例子
```
{
"id":"insert",
"widgettype":"Button",
"options":{
"width":"80px",
"i18n":true,
"label":"click me"
},
"binds":[
{
"wid":"self",
"event":"click",
"actiontype":"script",
"conform":{
"title":"conformtest",
"message":"测试事件处理前的用户确认功能,确认码?"
},
"target":"self",
"script":"alert('确认过眼神!')"
}
]
}
```
上述例子中定义了Button的click事件使用script事件处理方式来处理但是在处理前需要显示信息让用户确认是否继续如果用户放弃则不处理事件确认后正常的处理事件。

View File

@ -1,162 +0,0 @@
# Bricks 工厂模式技术文档
## 概述
`bricks.Factory` 是一个基于单例模式的工厂类,用于注册和获取组件(或“小部件”)对象。它提供了一个全局可访问的注册表,允许动态地注册命名的组件类或对象,并通过名称进行检索。
该实现是 `Bricks` 前端框架/库的一部分,旨在统一管理可复用的 UI 组件或其他功能模块。
---
## 代码结构
```javascript
var bricks = window.bricks || {};
class Factory_ {
constructor() {
this.widgets_kv = new Object();
this.widgets_kv['_t_'] = 1;
}
register(name, widget) {
this.widgets_kv[name] = widget;
}
get(name) {
if (this.widgets_kv.hasOwnProperty(name)) {
return this.widgets_kv[name];
}
return null;
}
}
bricks.Factory = new Factory_();
```
---
## 核心类:`Factory_`
### 构造函数 `constructor()`
初始化一个空的对象 `widgets_kv`,用于存储注册的组件。
#### 初始化内容:
- `this.widgets_kv = new Object()`
创建一个普通对象作为键值对存储容器。
- `this.widgets_kv['_t_'] = 1`
预留字段,可能用于调试、版本标识或检测对象是否已被初始化(用途未明确,但不影响主流程)。
> ⚠️ 注意:使用 `new Object()` 而非 `{}` 仅为风格选择,功能一致。
---
### 方法说明
#### `register(name: string, widget: any): void`
将指定名称与组件(类、函数或对象)注册到工厂中。
##### 参数:
| 参数名 | 类型 | 描述 |
|---------|--------|------|
| `name` | string | 注册的唯一名称(键),用于后续查找 |
| `widget`| any | 要注册的组件,通常为类构造函数或配置对象 |
##### 行为:
- 直接赋值:`this.widgets_kv[name] = widget`
- 若名称已存在,则覆盖旧值
##### 示例:
```js
class MyWidget { /* ... */ }
bricks.Factory.register('my-widget', MyWidget);
```
---
#### `get(name: string): any | null`
根据名称从工厂中获取已注册的组件。
##### 参数:
| 参数名 | 类型 | 描述 |
|---------|--------|------|
| `name` | string | 要查询的组件名称 |
##### 返回值:
- 如果存在该名称的组件,返回对应值;
- 否则返回 `null`
> 使用 `hasOwnProperty` 检查避免原型链污染风险。
##### 示例:
```js
const WidgetClass = bricks.Factory.get('my-widget');
if (WidgetClass) {
const instance = new WidgetClass();
}
```
---
## 全局实例:`bricks.Factory`
通过以下语句暴露为全局单例:
```js
bricks.Factory = new Factory_();
```
确保在整个应用中只有一个 `Factory` 实例,所有模块共享同一注册表。
> 利用 `window.bricks || {}` 确保命名空间安全,防止覆盖已有变量。
---
## 设计模式
- **单例模式Singleton Pattern**
`Factory_` 类仅被实例化一次,全局共享状态。
- **工厂模式Factory Pattern**
提供统一接口来创建对象,解耦对象的使用与创建过程。
---
## 使用场景
适用于需要动态加载或按需获取组件的系统,例如:
- UI 组件库注册与调用
- 插件系统
- 模块化前端架构中的依赖查找
---
## 注意事项
1. **名称冲突**
`register` 不检查重名,后注册者会覆盖前者,请确保命名唯一性。
2. **安全性**
当前未对输入参数做类型校验,建议在生产环境中添加防御性编程措施。
3. **性能**
使用普通对象作为 map在现代 JS 引擎中对于中等规模键集表现良好。若需大规模注册,可考虑改用 `Map`
4. **扩展建议**
- 可增加 `unregister(name)` 方法以支持注销组件
- 可增加 `list()` 方法返回所有注册项列表用于调试
---
## 版本信息
- 创建时间:未知(根据代码推断为早期 JS 模块化方案)
- 兼容性:支持 ES6+ 环境,可在浏览器中直接运行
---
## 总结
`bricks.Factory` 是一个轻量级、高效的组件注册与获取机制,适用于构建模块化、可扩展的前端系统。其简洁的设计使其易于理解和集成,是 `Bricks` 框架基础设施的重要组成部分。

View File

@ -1,321 +0,0 @@
# `bricks.IconBar` 及相关组件技术文档
---
## 概述
`bricks.IconBar` 是一个基于 `bricks.HBox` 的 UI 组件类,用于创建水平排列的图标工具栏。它支持动态图标、点击事件分发、响应式尺寸调整等功能。该模块还包含多个扩展类:
- `bricks.IconTextBar`:带文本标签的图标栏。
- `bricks.FloatIconBar`:浮动式图标栏,鼠标悬停时显示完整图标组。
- `bricks.FloatIconTextBar`:结合浮动与图文混合的增强型工具栏。
这些组件常用于构建轻量级、可定制的工具栏或操作面板。
---
## 基础结构
```js
var bricks = window.bricks || {};
```
确保 `bricks` 全局命名空间存在,避免覆盖已有内容。
---
## 1. `bricks.IconBar`
### 类继承关系
```js
class extends bricks.HBox
```
继承自 `HBox`(水平布局容器),实现横向排列子控件。
### 配置参数 (`opts`)
| 参数名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `cheight` | Number | `2` | 子项高度倍率(相对于字符单位) |
| `rate` | Number | `1` | 缩放比例因子 |
| `margin` | String | `'10px'` | 图标之间的左右边距 |
| `tools` | Array | `[]` | 工具定义数组,每个元素为一个工具描述对象 |
#### `tools` 数组项结构
| 属性 | 类型 | 是否必需 | 说明 |
|------|------|----------|------|
| `name` | String | 否 | 图标的唯一标识名称(用于事件分发和 DOM ID |
| `icon` | String | 是(非 blankicon | SVG 图标 URL 或资源路径 |
| `rate` | Number | 否 | 单独设置该图标的缩放比例(优先于全局 rate |
| `dynsize` | Boolean | 否 | 是否启用动态尺寸调整 |
| `tip` | String | 否 | 提示文本(暂未在 IconBar 中直接使用) |
> 特殊情况:若 `name === 'blankicon'`,则生成空白占位符图标。
### 构造函数行为
```js
constructor(opts)
```
1. **默认值填充**
- 若未指定 `cheight`,默认设为 `2`
- 若未指定 `rate`,默认设为 `1`
2. **调用父类构造器**
```js
super(opts);
```
3. **样式设置**
- 添加 CSS 类名 `'childrensize'`
- 设置主轴对齐方式为居中(`alignItems: center`
4. **遍历 `tools` 创建子控件**
- 复制每个工具配置并补充通用属性(如 `cheight`, `cwidth`, `dynsize`
- 调用 `build_item(opts)` 创建具体控件
- 设置左右 margin 和 cursor 样式为 pointer
- 若有 `name`,设置对应 widget 的 ID
- 绑定点击事件并通过 `regen_event` 分发事件
- 将控件加入容器
5. **内部高度记录**
- 使用 `this.height_int` 记录所有子控件中的最大高度(像素值),便于外部布局参考。
---
### 方法详解
#### `build_item(opts) → Widget`
根据配置创建单个图标控件。
##### 行为逻辑:
- 计算实际高度:`h = bricks.app.charsize * rate * opts.cheight`
- 更新 `this.height_int` 以保持最大高度
- 判断是否为 `blankicon`
- 是 → 返回 `new bricks.BlankIcon({...})`
- 否 → 使用 `opts.icon` 作为 `url` 创建 `bricks.Svg` 实例
- 绑定点击事件到 `regen_event(desc, event)`
- 返回创建的控件
#### `regen_event(desc, event)`
处理图标点击事件,并触发两个事件:
```js
this.dispatch(desc.name, desc); // 以 name 为事件类型分发
this.dispatch('command', desc); // 统一命令事件
```
同时阻止事件冒泡和默认行为:
```js
event.preventDefault();
event.stopPropagation();
```
> 此设计适用于菜单/按钮点击后不希望影响页面其他部分的操作场景。
---
## 2. `bricks.IconTextBar`(图文混合图标栏)
### 类继承关系
```js
class extends bricks.IconBar
```
重写了 `build_item` 方法以支持图标+文字组合。
### `build_item(opts)` 实现细节
返回一个 `HBox` 容器,内含:
1. **SVG 图标(可选)**
- 来源:`opts.icon`
- 属性包括 `rate`, `dynsize`
2. **文本标签(可选)**
- 使用 `bricks.Text` 显示 `opts.label`
- 支持国际化(`i18n: true`)、自动换行(`wrap: true`)、左对齐(`haligh: 'left'`
3. **整体包装**
- 包裹在一个 `HBox` 中,宽度自适应,高度自动计算
- 绑定点击事件至 `regen_event(opts)`
- 更新 `height_int`(基于 `charsize * rate`
> ⚠️ 注意:图标与文本共存时垂直居中对齐由外层 HBox 控制。
---
## 3. `bricks.FloatIconBar`(浮动图标栏)
### 类继承关系
```js
class extends bricks.HBox
```
实现“仅显示浮出按钮,悬停时展开全部图标”的交互模式。
### 配置参数 (`opts`)
`IconBar`,但只显示一个小浮点按钮,其余图标隐藏。
### 构造函数逻辑
1. 创建浮出按钮:
```js
this.float_w = new bricks.Svg({ url: bricks_resource('imgs/float-out.png') });
```
- 绑定 `mousemove` 事件显示图标栏
- 添加到容器
2. 创建图标主体:
```js
this.icons_box = this.build_bar(opts);
```
- 默认调用 `new bricks.IconBar(opts)`
- 初始隐藏(`display: none`
3. 设置容器高度等于 `icons_box.height_int`
4. 绑定全局点击事件隐藏图标栏(通过 `Body.click`
### 方法说明
| 方法 | 功能 |
|------|------|
| `build_bar(opts)` | 工厂方法,创建内部图标栏实例(可被子类重写) |
| `show_icons()` | 显示图标栏(`display: flex` |
| `hide_icons()` | 隐藏图标栏(`display: none` |
> 📌 交互逻辑:鼠标移上浮点按钮 → 显示图标;点击任意空白区域 → 隐藏图标。
---
## 4. `bricks.FloatIconTextBar`(浮动图文栏)
### 类继承关系
```js
class extends bricks.FloatIconBar
```
扩展 `FloatIconBar`,使其内部使用 `IconTextBar` 替代 `IconBar`
### 核心重写方法
```js
build_bar(opts) {
return new bricks.IconTextBar(opts);
}
```
> ✅ 效果:浮出后显示带文字说明的图标按钮组,适合需要语义化提示的场景。
---
## 工厂注册
以下语句将各组件注册到 `bricks.Factory`,支持通过字符串名称动态创建实例:
```js
bricks.Factory.register('IconBar', bricks.IconBar);
bricks.Factory.register('IconTextBar', bricks.IconTextBar);
bricks.Factory.register('FloatIconBar', bricks.FloatIconBar);
bricks.Factory.register('FloatIconTextBar', bricks.FloatIconTextBar);
```
### 使用示例(工厂方式)
```js
var bar = bricks.Factory.create('IconTextBar', {
tools: [
{ name: 'save', icon: '/icons/save.svg', label: 'Save File' },
{ name: 'print', icon: '/icons/print.svg', label: 'Print' }
],
margin: '8px',
rate: 1.2
});
```
---
## 示例配置
### 简单图标栏
```js
new bricks.IconBar({
cheight: 2,
rate: 1,
margin: '12px',
tools: [
{ name: 'home', icon: '/icons/home.svg', tip: 'Go Home' },
{ name: 'settings', icon: '/icons/settings.svg' },
{ name: 'blankicon' }, // 占位符
{ name: 'logout', icon: '/icons/logout.svg' }
]
});
```
### 图文混合栏
```js
new bricks.IconTextBar({
rate: 1.1,
tools: [
{ name: 'new', icon: '/icons/new.svg', label: 'New', tip: 'Create new file' },
{ name: 'open', icon: '/icons/open.svg', label: 'Open' },
{ name: 'help', label: 'Help', icon: '/icons/help.svg' }
]
});
```
### 浮动图文工具栏
```js
new bricks.FloatIconTextBar({
tools: [
{ name: 'cut', icon: '/icons/cut.svg', label: 'Cut' },
{ name: 'copy', icon: '/icons/copy.svg', label: 'Copy' },
{ name: 'paste', icon: '/icons/paste.svg', label: 'Paste' }
],
rate: 1
});
```
---
## 设计特点总结
| 特性 | 描述 |
|------|------|
| 🔧 **模块化设计** | 所有功能拆分为可复用类,便于扩展 |
| 🖱️ **交互友好** | 支持点击、悬停展开、事件拦截 |
| 📏 **响应式尺寸** | 基于 `charsize``rate` 实现相对缩放 |
| 🔔 **事件驱动** | 通过 `dispatch` 发送命名事件,解耦 UI 与业务逻辑 |
| 🧩 **工厂模式支持** | 可通过字符串动态创建组件 |
---
## 注意事项
1. **依赖前提**
- 必须已加载 `bricks.HBox`, `bricks.Svg`, `bricks.Text`, `bricks.BlankIcon`, `bricks.Body`
- `bricks.app.charsize` 需已初始化
- `bricks_resource()` 函数需可用(用于资源路径解析)
2. **性能建议**
- 工具数量较多时建议使用 `FloatIconBar` 减少视觉干扰
- 避免频繁重建整个 `IconBar`,应缓存实例
3. **样式定制**
- 推荐通过 `set_style()` 或外部 CSS 修改外观
- 不推荐直接修改内部 DOM 结构
---
## 版本信息
- **作者**Bricks Framework Team
- **版本**1.0
- **最后更新**2025年4月5日
---
📌 **文档结束**

View File

@ -1,353 +0,0 @@
# Bricks.js 表单模块技术文档
> 本文档描述了 `bricks` 框架中表单相关组件的实现逻辑、结构与使用方式。该模块提供了灵活的表单构建能力,支持动态字段渲染、数据校验、提交处理及工具栏集成。
---
## 目录
- [概述](#概述)
- [核心变量](#核心变量)
- [核心方法](#核心方法)
- [类说明](#类说明)
- [`FieldGroup`](#fieldgroup)
- [`FormBody`](#formbody)
- [`FormBase`](#formbase)
- [`InlineForm`](#inlineform)
- [`Form`](#form)
- [工厂注册](#工厂注册)
- [使用示例](#使用示例)
---
## 概述
本模块为 `bricks.js` 提供了一套完整的表单系统,包含以下功能:
- 动态生成表单字段(支持嵌套分组)
- 支持文件上传(需使用 `FormData`
- 自动化工具栏(提交、重置、取消按钮)
- 数据获取与验证
- 提交前后事件分发
- 多布局支持(水平/垂直输入框)
主要适用于 Web 端复杂表单场景,如配置页、编辑器、数据录入等。
---
## 核心变量
### `bricks.need_formdata_fields`
```js
bricks.need_formdata_fields = ['file', 'video', 'audio'];
```
**说明:**
指定哪些字段类型需要通过 `FormData` 提交(例如文件上传类字段)。这些字段将触发表单启用 `multipart/form-data` 编码模式。
---
## 核心方法
### `show_resp_message_or_error(resp)`
```js
bricks.show_resp_message_or_error = async function(resp){...}
```
**参数:**
- `resp`: `Response` 对象(来自 `fetch` 请求)
**功能:**
1. 解析响应体为 JSON。
2. 调用 `widgetBuild` 将返回的 UI 描述渲染到页面主体中。
**用途:**
用于处理服务端返回的动态 UI 或错误信息并展示。
---
## 类说明
### `FieldGroup`
负责根据字段定义递归构建表单控件。
#### 构造函数
```js
new FieldGroup(opts)
```
- `opts`: 配置对象(目前未实际使用)
#### 方法:`build_fields(form, parent, fields)`
| 参数 | 类型 | 说明 |
|----------|------------|------|
| `form` | Form 实例 | 当前所属表单对象 |
| `parent` | Widget | 容器组件(如 VBox/HBox |
| `fields` | Array | 字段数组 |
**行为说明:**
- 使用 `DynamicColumn` 实现响应式列布局(移动端默认两列)。
- 若遇到 `uitype: 'group'` 的字段,则递归构建其内部字段,并插入新列。
- 非隐藏字段创建 `VBox``HBox` 包裹标签和输入控件。
- 所有输入控件由 `Input.factory(field)` 创建,并存入 `form.name_inputs`
- 若字段类型在 `need_formdata_fields` 中,设置 `form.need_formdata = true`
---
### `FormBody`
继承自 `VScrollPanel`,是表单内容区域的核心容器。
#### 构造函数
```js
new FormBody(form, opts)
```
**初始化操作:**
- 设置宽高为 100%
- 初始化 `name_inputs` 映射
- 创建 `FieldGroup` 渲染非文本字段
- 调用 `build_text_fields()` 渲染文本字段
#### 方法:`build_text_fields()`
遍历 `form.textfields`,对每个字段:
- 创建标签文字 (`Text`)
- 创建只读文本控件 (`UiText`)
- 包装成 `VBox` 并添加至容器
- 注册到 `form.name_inputs` 中以便后续取值
> **注意:** `text` 类型字段不参与编辑,仅用于显示。
---
### `FormBase`
表单基类,提供通用功能(继承自 `Layout`)。
#### 属性
| 属性名 | 类型 | 说明 |
|-------------------|--------|------|
| `name_inputs` | Object | 存储字段名 → 控件实例映射 |
| `submit_changed` | Boolean| 是否仅提交变更字段 |
| `origin_data` | Object | 初始数据快照(用于比对变化) |
| `submit_url` | String | 提交地址 |
| `method` | String | HTTP 方法,默认 `'POST'` |
| `toolbar` | Object | 工具栏配置扩展 |
#### 方法
##### `build_toolbar(widget)`
动态构建顶部工具栏(含“提交”、“重置”、“取消”按钮),支持自定义扩展。
- 默认图标路径由 `bricks_resource()` 加载。
- 使用 `IconTextBar` 组件呈现按钮条。
- 绑定 `command` 事件到 `command_handle`
##### `command_handle(event)`
处理工具栏命令事件:
| 命令名 | 行为 |
|-----------|------|
| `submit` | 触发 `validation()` |
| `reset` | 调用 `reset_data()` |
| `cancel` | 触发 `dispatch('cancel')` |
| 其他 | 若有 `action` 则执行事件处理器;否则直接 `dispatch(name)` |
##### `reset_data()`
重置所有输入控件为其初始值(调用各控件的 `reset()` 方法)。
##### `_getValue()`
收集所有字段值,进行必填校验:
- 忽略无权访问的属性(`hasOwnProperty`
- 调用 `getValue()` 获取控件值
- 必填项为空时弹出错误提示并聚焦
- 返回合并后的数据对象
> ⚠️ 若校验失败则中断并返回 `undefined`
##### `getValue()`
优先返回缓存的 `this.data`,否则调用 `get_formdata()`
##### `get_formdata()`
构建 `FormData` 实例:
- 遍历所有控件,调用 `set_formdata(data)` 写入数据
- 若启用了 `submit_changed`,跳过未修改字段
- 忽略值为 `null` 的字段
- 返回 `FormData``null`(无变更)
##### `validation()`
异步提交流程控制:
1. 显示加载动画(`Running` 组件)
2. 获取 `FormData`
3. 分发 `submit` 事件
4. 若设置了 `submit_url`,发起 HTTP 请求
5. 成功后分发 `submited` 事件,传入响应对象
6. 出错时捕获异常并输出日志
7. 隐藏加载动画
##### `save_origin_data()`
保存当前所有字段值作为原始状态,用于后续变更判断。
---
### `InlineForm`
轻量级内联表单,适合嵌入其他组件内部。
#### 特性:
- 不包含标题或描述
- 直接渲染字段 + 工具栏
- 自动保存初始数据
#### 构造函数
```js
new InlineForm(opts)
```
**行为:**
- 设置宽高 100%,开启滚动
- 调用 `FieldGroup.build_fields()` 渲染 `opts.fields`
- 添加工具栏到第一个子容器
- 保存初始数据
---
### `Form`
完整表单组件,具备标题、描述、分栏、工具栏等功能。
#### 配置选项(`opts`
| 参数 | 类型 | 说明 |
|----------------|----------|------|
| `title` | String | 表单标题(可国际化) |
| `description` | String | 表单描述文本 |
| `notoolbar` | Boolean | 是否隐藏工具栏,默认 `false` |
| `input_layout` | String | 输入框布局 `"VBox"`(默认)或 `"HBox"` |
| `fields` | Array | 字段定义数组 |
| `submit_url` | String | 提交 URL |
| `method` | String | HTTP 方法GET/POST |
| `toolbar` | Object | 自定义工具栏配置 |
#### 构造流程
1. 设置尺寸与滚动
2. 添加标题(若有)
3. 添加描述(若有)
4. 添加填充容器 `Filler`
5. 分离 `text` 与其他字段
6. 创建 `FormBody` 渲染主体内容
7. 条件性构建工具栏
8. 保存初始数据
---
## 工厂注册
```js
bricks.Factory.register('InlineForm', bricks.InlineForm);
bricks.Factory.register('Form', bricks.Form);
```
允许通过字符串名称动态创建组件实例,便于模板化渲染。
---
## 使用示例
### 定义一个简单表单
```js
var form = new bricks.Form({
title: "User Profile",
description: "Please fill in your information.",
submit_url: "/api/user/update",
method: "POST",
input_layout: "VBox",
fields: [
{ name: "username", label: "Username", uitype: "text_input", required: true },
{ name: "email", label: "Email", uitype: "email", required: true },
{ name: "avatar", label: "Avatar", uitype: "file" },
{ name: "bio", label: "Bio", uitype: "textarea" },
{
uitype: "group",
fields: [
{ name: "phone", label: "Phone", uitype: "tel" },
{ name: "age", label: "Age", uitype: "number" }
]
}
]
});
// 监听提交事件
form.bind('submit', function(data){
console.log("Submitting:", data);
});
form.bind('submited', function(resp){
alert("Saved successfully!");
});
```
### 内联表单示例
```js
var inline = new bricks.InlineForm({
fields: [
{ name: "name", label: "Name", uitype: "text_input" },
{ name: "status", label: "Status", uitype: "select", options: [...] }
],
toolbar: {
tools: [
{ icon: "...", name: "custom", label: "Custom Action", action: "doSomething" }
]
}
});
inline.bind('command:custom', function(){ ... });
```
---
## 附录:字段定义格式
```json
[
{
"name": "field_name",
"label": "Display Label",
"uitype": "text_input|email|file|group|hide|...",
"required": true,
"value": "default_value",
"removable": false,
"icon": "optional_icon_path"
}
]
```
其中:
- `group`: 可嵌套子字段
- `text`: 仅展示,不可编辑
- `hide`: 隐藏字段(但仍会渲染占位)
---
## 注意事项
- 文件上传字段必须使用 `FormData`,确保 `submit_url` 接口支持 `multipart/form-data`
- 所有文本标签建议启用 `i18n: true` 实现多语言支持。
- 自定义控件需实现 `getValue()``set_formdata(formData)` 接口。
- 错误提示依赖 `bricks.Error` 组件,请确保已加载。
---
> 📚 更多组件参考请查阅 [Bricks.js 官方文档](https://github.com/your-bricks-project)。

View File

@ -1,283 +0,0 @@
# `bricks.Gobang` 技术文档
> **五子棋游戏组件** —— 基于 `bricks.js` 框架实现的可交互五子棋界面控件。
---
## 目录
- [概述](#概述)
- [类结构](#类结构)
- [`bricks.GobangPoint`](#bricksgobangpoint)
- [`bricks.Gobang`](#bricksgobang)
- [API 参考](#api-参考)
- [`GobangPoint` 构造函数与方法](#gobangpoint-构造函数与方法)
- [`Gobang` 构造函数与方法](#gobang-构造函数与方法)
- [事件绑定](#事件绑定)
- [图像资源管理](#图像资源管理)
- [布局与自适应](#布局与自适应)
- [使用示例](#使用示例)
---
## 概述
`bricks.Gobang` 是一个基于 `bricks.js` UI 框架开发的五子棋Gomoku游戏界面组件。它包含两个核心类
- `bricks.GobangPoint`:表示棋盘上的单个格子,用于显示空、黑子或白子状态,并支持鼠标悬停反馈。
- `bricks.Gobang`:主控件,负责构建 15×15 的棋盘布局、管理棋子状态、响应尺寸变化并为后续 AI/玩家逻辑提供接口。
该组件具备良好的可扩展性,可用于人机对战、双人对战或多智能体博弈系统。
---
## 类结构
### `bricks.GobangPoint`
继承自 `bricks.Image`,代表棋盘上一个可交互的点位。
#### 属性说明
| 属性名 | 类型 | 描述 |
|-----------|--------|------|
| `p_status` | Number | 棋子状态:`0`=空, `1`=黑子, `2`=白子 |
| `p_x` | Number | 横向坐标(列),取值范围 `115` |
| `p_y` | Number | 纵向坐标(行),取值范围 `115` |
> 注:坐标系以左上角为 `(1,1)`,右下角为 `(15,15)`
#### 构造函数
```js
new bricks.GobangPoint({
p_status: 0,
p_x: x,
p_y: y,
tip: '(x,y)'
})
```
##### 参数
| 参数 | 类型 | 必填 | 说明 |
|----------|--------|------|------|
| `p_status` | Number | 是 | 初始状态 |
| `p_x` | Number | 是 | X 坐标 |
| `p_y` | Number | 是 | Y 坐标 |
| `tip` | String | 否 | 鼠标提示文本,默认为 `(x,y)` |
#### 方法
##### `.calc_url()`
根据当前 `p_status`, `p_x`, `p_y` 计算对应的图片路径。
- 边缘位置使用特定前缀:
- 上边:`t`
- 下边:`b`
- 左边:`l`
- 右边:`r`
- 中间区域:`c`
- 状态后缀:
- `empty`: 空格
- `black`: 黑子
- `white`: 白子
**输出格式:**
```
imgs/[one][two]_[state].png
```
例如:`imgs/tl_empty.png`, `imgs/cc_black.png`
> 使用 `bricks_resource(name)` 获取实际资源 URL。
##### `.set_current_position(flg, event)`
处理鼠标进入/离开事件,设置 CSS 类控制视觉高亮。
- `flg === true` → 移除 `curpos` 样式类(显示高亮)
- `flg === false` → 添加 `curpos` 类(取消高亮)
> 实际行为依赖于样式表中 `.curpos` 的定义。
##### `.getValue()`
返回当前点的状态对象。
```js
{
status: this.p_status,
x: this.p_x,
y: this.p_y
}
```
##### `.str()`
调试用字符串输出,格式为 `(status,x,y)`
---
### `bricks.Gobang`
主游戏容器,继承自 `bricks.VBox`,自动创建 15×15 棋盘。
#### 构造函数
```js
new bricks.Gobang(opts)
```
初始化棋盘,渲染空白格子,并开始第一轮回合(默认黑方先行)。
#### 内部属性
| 属性 | 类型 | 说明 |
|----------|----------|------|
| `filler` | Filler | 占位容器,用于监听尺寸变化 |
| `area` | Array[Array[GobangPoint]] | 二维数组,存储所有 `GobangPoint` 实例 |
| `player` | Object | (预留)玩家配置信息 |
##### 玩家配置结构(未来扩展)
```js
{
black_player: {
name: "Player1",
type: "user", // 或 "llm"
url: "", // AI 接口地址
delay: 1, // 响应延迟(秒)
params: {} // 自定义参数
},
white_player: { ... }
}
```
#### 方法
##### `.render_empty_area()`
创建 15×15 的棋盘网格:
- 创建外层垂直盒子 (`VBox`)
- 每行新建一个水平盒子 (`HBox`)
- 在每个格子中添加 `GobangPoint(p_status=0)`
- 将其组织成二维数组 `this.area[i][j]`,其中 `i = p_y - 1`, `j = p_x - 1`
最终将整个棋盘加入 `this.filler` 容器。
##### `.resize_area()`
响应容器大小变化,动态调整每个棋盘点的宽高,保持正方形且填满可用空间。
- 计算最小边长:`Math.min(clientWidth, clientHeight)`
- 单元格尺寸:`size = min_side / 15`
- 遍历 `this.area` 设置每个 `GobangPoint` 的样式宽高
> 绑定在 `filler``element_resize` 事件上。
##### `.inform_go(party)`
通知某一方落子(如 `'black'``'white'`)。此方法目前为空,作为钩子供子类或外部逻辑重写。
典型用途:
- 触发 AI 思考
- 更新 UI 提示
- 发送网络请求
---
## 事件绑定
| 元素 | 事件 | 回调 | 说明 |
|------|------|-------|------|
| `GobangPoint` | `mouseover` | `set_current_position(true)` | 显示悬停效果 |
| `GobangPoint` | `mouseout` | `set_current_position(false)` | 隐藏悬停效果 |
| `filler` | `element_resize` | `resize_area()` | 自动适配棋盘尺寸 |
---
## 图像资源管理
通过 `bricks_resource(name)` 函数加载图像资源。建议准备以下命名规则的 PNG 图片:
```
imgs/
├── tl_empty.png ── 左上角空格
├── tc_empty.png ── 上边缘中间空格
├── tr_empty.png ── 右上角空格
├── cl_empty.png ── 左边缘中间空格
├── cc_empty.png ── 中央空格
├── cr_empty.png ── 右边缘中间空格
├── bl_empty.png ── 左下角空格
├── bc_empty.png ── 下边缘中间空格
├── br_empty.png ── 右下角空格
├── **_black.png ── 所有位置的黑子版本
└── **_white.png ── 所有位置的白子版本
```
> 共需 9 种位置 × 3 种状态 = 27 张图(若四角合并可减少)。
---
## 布局与自适应
- 使用 `bricks.Filler` 包裹棋盘以监听 DOM 尺寸变化。
- `resize_area()` 方法确保棋盘始终居中并等比缩放。
- 所有点位统一设为正方形,避免拉伸失真。
- 支持响应式设计,在不同屏幕尺寸下正常显示。
---
## 使用示例
```js
// 创建五子棋组件并插入页面
var gobang = new bricks.Gobang({});
document.body.appendChild(gobang.dom_element);
// (可选)手动更改某个点状态
var point = gobang.area[7][7]; // 中心点
point.p_status = 1;
point.set_url(point.calc_url()); // 重新计算图像
```
> 若需集成 AI 对战,请扩展 `inform_go` 方法以调用远程服务或本地模型。
---
## 注册与框架集成
```js
bricks.Factory.register('Gobang', bricks.Gobang);
```
允许通过工厂模式动态创建实例:
```js
var game = bricks.Factory.create('Gobang', {});
```
---
## 待完善功能TODO
- ✅ 棋盘渲染与自适应
- ✅ 棋子状态切换
- ⬜ 落子逻辑检测(是否已占用)
- ⬜ 胜负判断(五连珠算法)
- ⬜ 回合控制与玩家切换
- ⬜ AI 对手集成LLM 或规则引擎)
- ⬜ 悔棋、重新开始等功能按钮
---
## 版权与许可
© 2025 bricks.js 项目组
本模块遵循 MIT 许可协议,欢迎自由使用与修改。

View File

@ -1,123 +0,0 @@
# `bricks.Html` 组件技术文档
## 概述
`bricks.Html` 是 Bricks 框架中的一个基础 UI 组件,用于将原始 HTML 内容动态插入到 DOM 中。它继承自 `bricks.JsWidget`,是构建更复杂组件或直接渲染静态/动态 HTML 内容的轻量级工具。
---
## 类定义
```javascript
bricks.Html = class extends bricks.JsWidget
```
- **继承自**: `bricks.JsWidget`
- **用途**: 渲染传入的 HTML 字符串到组件的根 DOM 元素中。
- **注册名称**: `'Html'`(通过 `bricks.Factory` 注册)
---
## 构造函数
### `constructor(opts)`
初始化 `Html` 组件实例。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `opts` | Object | 配置选项对象,继承自 `JsWidget` 并扩展以下字段。 |
#### `opts` 属性
| 属性名 | 类型 | 必填 | 说明 |
|--------|--------|------|------|
| `html` | String | 是 | 要插入到组件内部的 HTML 字符串内容。该内容将直接设置为 `innerHTML`。 |
#### 示例
```javascript
const htmlWidget = new bricks.Html({
html: '<div class="info">这是一段 <strong>HTML</strong> 内容</div>'
});
```
---
## 功能说明
- 在构造函数中调用 `super(opts)` 初始化父类 `JsWidget` 的基本功能(如 DOM 创建、事件绑定等)。
- 使用 `this.dom_element.innerHTML = opts.html` 将传入的 HTML 字符串注入组件的根元素。
> ⚠️ **安全提示**:由于使用了 `innerHTML`,若 `html` 字段包含用户输入,可能存在 XSS 安全风险。建议在使用前对 HTML 内容进行过滤或转义。
---
## 工厂注册
```javascript
bricks.Factory.register('Html', bricks.Html);
```
- 将 `Html` 组件注册到 Bricks 的工厂系统中,允许通过配置方式(如 JSON 描述)创建该组件。
- 注册名称为 `'Html'`,可在模板或布局定义中使用。
#### 工厂创建示例(假设支持 JSON 配置)
```json
{
"type": "Html",
"html": "<p>通过工厂创建的 HTML 组件</p>"
}
```
---
## 使用示例
### 基本用法
```javascript
// 创建一个包含 HTML 内容的组件
const myHtmlComponent = new bricks.Html({
html: `
<h3>欢迎使用 Bricks Html 组件</h3>
<ul>
<li>支持任意合法 HTML</li>
<li>可嵌入富文本内容</li>
</ul>
`
});
// 将组件添加到页面某容器中
document.getElementById('container').appendChild(myHtmlComponent.dom_element);
```
---
## 注意事项
1. **安全性**:避免将不可信的用户输入直接赋值给 `html` 字段。
2. **脚本执行**:通过 `innerHTML` 插入的 `<script>` 标签默认不会执行,如需执行需额外处理。
3. **样式隔离**:插入的内容会继承父级样式,建议使用 CSS 类名控制样式作用域。
---
## 相关类
- [`bricks.JsWidget`](./JsWidget.md) —— 所有 JavaScript 组件的基类。
- [`bricks.Factory`](./Factory.md) —— 组件工厂,用于注册和实例化组件。
---
## 版本信息
- **框架**: Bricks UI Framework
- **模块**: `bricks.Html`
- **最后更新**: _可根据实际项目填写_
---
✅ 适用于快速渲染静态 HTML 内容场景,是构建富文本展示、帮助文档、动态提示等模块的理想选择。

View File

@ -1,283 +0,0 @@
下面是为提供的 JavaScript 代码编写的 **Markdown 格式技术文档**,包含类的功能说明、构造函数参数、方法详解以及使用示例。
---
# `bricks.I18n` 国际化支持类
`bricks.I18n` 是一个轻量级的国际化i18n工具类用于在前端应用中实现多语言文本翻译和动态语言切换。它支持从远程 URL 加载语言包,并缓存已加载的语言资源以提高性能。
该类依赖于全局对象 `window.bricks`,并使用 `bricks.HttpJson` 进行异步请求,同时假设存在辅助函数 `objget``obj_fmtstr` 用于对象操作和字符串格式化。
---
## 基本结构
```javascript
var bricks = window.bricks || {};
bricks.I18n = class {
// 实现见下文
};
```
> ⚠️ 注意:此代码片段中存在一处变量名错误(`opts` 未定义),已在文档中指出并修正。
---
## 构造函数
### `new bricks.I18n(options)`
初始化 I18n 实例,配置语言资源路径与默认行为。
#### 参数
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `url` | `string` | 否 | 获取语言包的 API 地址(如 `/api/i18n`)。若不提供,则仅支持手动设置字典。 |
| `method` | `string` | 否 | 请求语言包时使用的 HTTP 方法,默认为 `'GET'`。 |
| `default_lang` | `string` | 否 | 默认语言代码(如 `'en'`, `'zh-CN'`),默认值为 `'en'`。 |
#### 示例
```js
const i18n = new bricks.I18n({
url: '/api/translations',
default_lang: 'zh-CN'
});
```
> ❌ **注意原始代码 Bug**
> 原始代码中使用了未定义的 `opts` 变量:
> ```js
> this.url = opts.url;
> ```
> 应改为传入的构造函数参数 `url`, `method`, `default_lang`。**正确实现应如下:**
```js
constructor(url, default_lang, method) {
this.url = url;
this.default_lang = default_lang || 'en';
this.method = method || 'GET';
this.lang_msgs = {}; // 缓存所有语言的消息字典
this.msgs = {}; // 当前激活语言的翻译映射
}
```
或者接受一个选项对象:
```js
constructor(options) {
this.url = options.url;
this.default_lang = options.default_lang || 'en';
this.method = options.method || 'GET';
this.lang_msgs = {};
this.msgs = {};
}
```
> ✅ 文档后续基于 **修正后的构造函数** 进行描述。
---
## 方法
### `_ (txt, obj) → string`
获取指定键的翻译文本,并可选地进行变量替换。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `txt` | `string` | 要翻译的键名key。 |
| `obj` | `Object``undefined` | 用于填充模板字符串的变量对象。 |
#### 返回值
- 如果当前语言中有对应的翻译,返回翻译后的内容;
- 若提供了 `obj`,则调用 `obj_fmtstr(obj, translatedStr)` 对结果进行格式化;
- 否则返回原始 `txt`
#### 示例
```js
i18n.setup_dict({
"hello": "Hello, {name}!",
"welcome": "Welcome aboard."
}, 'en');
i18n._("hello", {name: "Alice"});
// 输出: "Hello, Alice!"
i18n._("welcome");
// 输出: "Welcome aboard."
```
---
### `is_loaded(lang) → boolean`
检查某个语言包是否已被加载到内存中。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `lang` | `string` | 语言代码(如 `'zh-CN'`)。 |
#### 返回值
- `true`:该语言的消息字典已加载;
- `false`:尚未加载。
#### 示例
```js
if (!i18n.is_loaded('fr')) {
await i18n.change_lang('fr');
}
```
---
### `setup_dict(dic, lang)`
将给定的语言字典注册到实例中,并立即激活该语言。
> 此方法通常由 `change_lang` 内部调用,也可用于预加载或静态注入语言包。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `dic` | `Object` | 键值对形式的语言翻译表,例如 `{ "ok": "确定" }`。 |
| `lang` | `string` | 该字典对应的语言代码。 |
#### 行为
- 将 `dic` 存储至 `this.lang_msgs[lang]` 中;
- 设置 `this.msgs = dic`,即当前活跃语言为 `lang`
#### 示例
```js
i18n.setup_dict({
"title": "应用标题",
"save": "保存"
}, 'zh-CN');
```
---
### `async change_lang(lang) → Promise<void>`
切换当前语言环境。如果目标语言未加载,会自动通过网络请求获取。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `lang` | `string` | 目标语言代码(如 `'es'`, `'ja'`)。 |
#### 流程
1. 检查是否已加载该语言:
- 是 → 直接激活;
- 否 → 发起 HTTP 请求获取语言包。
2. 使用 `bricks.HttpJson` 发送请求,携带参数 `{ lang }`
3. 成功响应后,调用 `setup_dict` 注册并激活新语言。
#### 异常处理
- 若未设置 `this.url`,则跳过请求,无法加载新语言。
- 错误需由 `HttpJson` 层自行抛出或捕获。
#### 示例
```js
await i18n.change_lang('zh-CN');
document.title = i18n._('app_title'); // 使用新语言更新界面
```
---
## 依赖说明
本类依赖以下外部模块或函数:
| 名称 | 说明 |
|------|------|
| `bricks.HttpJson` | 用于发送 JSON 格式的 HTTP 请求,需具备 `httpcall(url, config)` 方法。 |
| `objget(obj, path)` | 工具函数,安全访问嵌套对象属性(类似 lodash.get。 |
| `obj_fmtstr(obj, str)` | 字符串模板替换函数,支持 `{key}` 语法,用 `obj.key` 替换。 |
> 示例 `obj_fmtstr` 实现:
> ```js
> function obj_fmtstr(obj, str) {
> return str.replace(/\{([^}]+)\}/g, (m, key) => obj[key] || m);
> }
> ```
---
## 使用场景示例
### 初始化与语言切换
```js
// 初始化 i18n 实例
const i18n = new bricks.I18n({
url: '/api/translations',
default_lang: 'en'
});
// 切换语言
async function setLanguage(lang) {
await i18n.change_lang(lang);
updateUI(); // 重新渲染页面文本
}
function updateUI() {
document.getElementById('greeting').textContent = i18n._('welcome', { user: 'Tom' });
}
```
### 预加载语言包(离线模式)
```js
// 手动注入英文包
i18n.setup_dict({
"welcome": "Welcome!",
"goodbye": "Goodbye!"
}, 'en');
// 不发起请求,直接切换
i18n.change_lang('en').then(updateUI);
```
---
## 注意事项
1. ✅ **修复建议**:原始代码中的 `opts` 应为构造函数参数对象,请确保传参方式一致。
2. 🔐 安全性:确保服务端返回的语言包内容经过清洗,防止 XSS。
3. 🌐 性能优化:所有语言包会被缓存,避免重复请求。
4. ⏱ 异步加载:首次切换未加载语言时需要等待网络响应,请添加加载状态提示。
---
## 版本历史(示例)
| 版本 | 修改内容 |
|------|----------|
| 0.1 | 初始版本,支持基本翻译与远程加载 |
---
## 许可证
MIT License
---
✅ *文档结束*

View File

@ -1,218 +0,0 @@
# `IconbarPage` 技术文档
> **模块路径**: `bricks.IconbarPage`
> **继承自**: `bricks.VBox`
> **用途**: 提供一个带有图标工具栏的页面容器,支持顶部或底部布局,并可动态加载内容组件。
---
## 概述
`bricks.IconbarPage` 是一个基于 `bricks.VBox` 的复合组件,用于构建具有图标文本工具栏(`IconTextBar`)和内容区域的页面。工具栏可置于页面顶部或底部,点击工具项时会触发对应的内容展示逻辑。
该组件适用于需要导航式界面的应用场景,如设置页、功能面板或多标签界面。
---
## 类定义
```javascript
bricks.IconbarPage = class extends bricks.VBox { ... }
```
---
## 构造函数
### `constructor(opts)`
初始化 `IconbarPage` 实例。
#### 参数
| 参数名 | 类型 | 说明 |
|-------|------|------|
| `opts` | Object | 配置选项对象 |
##### `opts` 结构
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `bar_opts` | Object | 是 | - | 图标工具栏的配置选项,传递给 `bricks.IconTextBar` |
| `bar_at` | String | 否 | `'top'` | 工具栏位置,可选 `'top'``'bottom'` |
| `height` | String | 否 | `'100%'` | 自动设置为 100%,确保占满父容器高度 |
##### `bar_opts` 子属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `margin` | Number/String | 工具栏外边距 |
| `rate` | Number | 布局占比(在 VBox 中使用的伸缩比例) |
| `tools` | Array\<Tool\> | 工具项列表,每个工具项定义一个可点击的操作 |
##### `tools` 数组中的 `tool` 对象结构
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | String | 是 | 工具名称,作为唯一标识 |
| `icon` | String | 是 | 图标类名(如 Font Awesome 类名) |
| `label` | String | 否 | 显示文本(可选) |
| `tip` | String | 是 | 鼠标悬停提示文本 |
| `dynsize` | Boolean | 否 | 是否动态调整大小 |
| `rate` | Number | 否 | 在布局中所占比例 |
| `context` | Object | 否 | 附加上下文数据,可用于命令处理 |
| `content` | WidgetConfig / Function / Promise | 是 | 要加载的内容组件配置或异步构造逻辑 |
---
## 成员属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `bar_at` | String | 存储工具栏位置(`'top'``'bottom'` |
| `content` | `bricks.Filler` | 内容容器,用于动态插入当前选中的工具对应的内容组件 |
| `bar` | `bricks.IconTextBar` | 创建的图标文本工具栏实例 |
---
## 方法
### `command_handle(event) → Promise<void>`
处理来自工具栏的命令事件(例如点击某个工具按钮),并加载对应的内容。
#### 参数
- `event`: 事件对象
- `params`: 触发事件的 `tool` 对象(即被点击的工具项)
#### 行为
1. 获取触发事件的 `tool`
2. 调用 `show_content(tool)` 异步加载内容
> ⚠️ 使用 `bind(this)` 绑定上下文,在构造时通过 `bar.bind('command', ...)` 注册。
---
### `show_content(tool) → Promise<void>`
根据传入的 `tool` 动态创建并显示其对应的内容组件。
#### 参数
- `tool`: 工具项对象(包含 `content` 字段)
#### 流程
1. 使用 `bricks.widgetBuild(tool.content, this)` 异步生成组件实例
2. 若组件存在且尚未添加到 DOM 树,则将其加入 `this.content` 容器
3. 替换当前内容区域的内容(旧内容会被自动移除?取决于 `Filler` 实现)
> ✅ 支持异步组件加载,适合懒加载或远程模块。
---
## 生命周期与初始化行为
1. 设置默认高度为 `'100%'`
2. 确定工具栏位置(默认为 `'top'`
3. 调用父类 `super(opts)` 初始化 VBox 布局
4. 创建 `IconTextBar` 实例并传入 `bar_opts`
5. 创建 `Filler` 作为内容占位符
6. 根据 `bar_at` 决定工具栏与内容的排列顺序:
- `'top'`: 先工具栏,后内容
- `'bottom'`: 先内容,后工具栏
7. 绑定 `command` 事件监听器到 `command_handle`
8. 使用 `schedule_once` 延迟 0.1 秒自动显示第一个工具项的内容
> 🕒 `schedule_once(..., 0.1)` 可避免渲染冲突,确保 DOM 就绪后再加载初始内容。
---
## 示例用法
```javascript
var page = new bricks.IconbarPage({
bar_at: 'top',
bar_opts: {
margin: 5,
rate: 0,
tools: [
{
name: 'dashboard',
icon: 'fa fa-tachometer',
label: '仪表盘',
tip: '查看系统状态',
rate: 1,
content: { type: 'DashboardWidget', opts: {} }
},
{
name: 'settings',
icon: 'fa fa-cog',
tip: '系统设置',
content: async function() {
let mod = await import('./SettingsPanel.js');
return new mod.SettingsPanel();
}
}
]
}
});
document.body.appendChild(page.root);
```
---
## 事件机制
- **事件类型**: `command`
- **触发源**: `bricks.IconTextBar`
- **携带参数**: `event.params` 包含完整的 `tool` 对象
- **监听方式**: 在 `IconTextBar` 上绑定 `command` 事件
---
## 注册信息
```javascript
bricks.Factory.register('IconbarPage', bricks.IconbarPage);
```
可通过工厂方法创建:
```javascript
bricks.Factory.create('IconbarPage', {...opts});
```
---
## 注意事项
1. **内容清理**:当前实现未显式清除旧内容,依赖 `Filler` 组件的行为。若需手动管理,请扩展 `show_content` 方法。
2. **性能优化**:首次加载延迟 0.1s,建议确认是否必要;可考虑使用 `requestAnimationFrame` 或更精确的时机控制。
3. **错误处理**`widgetBuild` 失败时无异常捕获,建议包裹 `try-catch` 增强健壮性。
---
## 依赖组件
- `bricks.VBox` —— 垂直布局容器
- `bricks.IconTextBar` —— 图标+文本工具栏
- `bricks.Filler` —— 内容填充容器
- `bricks.widgetBuild` —— 异步构建组件的工具函数
- `schedule_once` —— 延迟执行工具函数(通常为 `setTimeout` 封装)
---
## 版本历史
| 日期 | 修改人 | 说明 |
|------|--------|------|
| 2025-04-05 | AutoDoc Generator | 根据源码生成技术文档 |
---
**文档完成**
📌 推荐配合 `IconTextBar` 文档一起阅读以理解完整交互逻辑。

View File

@ -1,184 +0,0 @@
# Bricks 框架Iframe 与 NewWindow 组件技术文档
> **版本**1.0
> **作者**Bricks 团队
> **适用框架**Bricks.js基于类的前端组件架构
---
## 概述
本文档描述 `bricks` 命名空间下的两个核心组件:
- `bricks.Iframe`:用于在当前页面嵌入外部网页内容的 `<iframe>` 组件。
- `bricks.NewWindow`:用于打开新浏览器窗口并加载指定 URL 的组件。
这两个组件均继承自 `bricks` 框架的基础类,并通过 `bricks.Factory` 注册,支持动态实例化。
---
## 依赖说明
- 需确保全局存在 `window.bricks` 对象(或自动初始化)。
- 依赖基础类:
- `bricks.Layout`:用于 `Iframe`
- `bricks.JsWidget`:用于 `NewWindow`
- 工厂模式注册依赖:`bricks.Factory.register`
---
## 1. `bricks.Iframe`
### 继承关系
```js
class Iframe extends bricks.Layout
```
### 功能说明
创建一个内联框架(`<iframe>`),将指定 URL 的内容嵌入当前页面。默认高度为 `100%`,可自定义。
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.url` | `String` | ✅ | 要嵌入的网页 URL |
| `opts.height` | `String` | ❌ | iframe 高度(默认 `'100%'` |
| `opts` 中其他属性 | — | — | 将传递给父类 `bricks.Layout` |
#### 示例
```js
const iframe = new bricks.Iframe({
url: 'https://example.com',
height: '500px'
});
```
### 方法
#### `create()`
创建 DOM 元素 `<iframe>` 并赋值给 `this.dom_element`
```js
create()
```
- **行为**
- 创建一个新的 `HTMLIFrameElement`
- 不自动添加到 DOM需由父类或外部逻辑处理挂载。
---
## 2. `bricks.NewWindow`
### 继承关系
```js
class NewWindow extends bricks.JsWidget
```
### 功能说明
调用浏览器原生 `window.open()` 方法,在新窗口或标签页中打开指定 URL。
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.url` | `String` | ✅ | 要打开的网页地址 |
| `opts.name` | `String` | ❌ | 新窗口名称(参见 `window.open` 第二参数),默认 `'_blank'` |
| `opts` 中其他属性 | — | — | 传递给父类 `bricks.JsWidget` |
#### 示例
```js
new bricks.NewWindow({
url: 'https://example.com',
name: 'myWindow'
});
// 打开新窗口,名称为 'myWindow'
new bricks.NewWindow({
url: 'https://help.com'
});
// 使用默认设置打开新标签页
```
> ⚠️ 注意:现代浏览器可能阻止未用户触发的 `window.open()` 调用(如非点击事件中执行)。
---
## 3. 工厂注册
为支持动态创建组件,两个类均已注册到 `bricks.Factory`
```js
bricks.Factory.register('NewWindow', bricks.NewWindow);
bricks.Factory.register('Iframe', bricks.Iframe);
```
### 使用工厂创建实例(示例)
```js
const widget = bricks.Factory.create('Iframe', {
url: 'https://dashboard.example.com',
height: '600px'
});
document.body.appendChild(widget.getElement()); // 假设方法存在
```
---
## 使用场景建议
| 组件 | 推荐场景 |
|------|----------|
| `Iframe` | 嵌入第三方仪表盘、帮助文档、跨域内容等需要隔离但显示在当前页的内容 |
| `NewWindow` | 跳转外部链接、打开帮助中心、弹出独立操作界面等无需嵌入当前页面的操作 |
---
## 注意事项
1. **安全性**
- 使用 `<iframe>` 时注意 XSS 和点击劫持风险,建议对源站点设置 CSP 策略。
- 避免在 `iframe` 中加载不可信来源。
2. **兼容性**
- `window.open()` 受浏览器弹窗拦截机制影响,请确保在用户交互上下文中调用。
3. **响应式设计**
- `Iframe` 默认高度为 `100%`,请确保其容器具有明确高度,否则可能无法正确渲染。
4. **SEO 与可访问性**
- `<iframe>` 内容不被搜索引擎直接索引,重要信息应避免仅通过 iframe 展示。
- 添加 `title` 属性以提升可访问性(当前代码未实现,建议扩展)。
---
## 扩展建议
```js
// 示例:增强 Iframe 支持 title 和 sandbox 属性
class EnhancedIframe extends bricks.Iframe {
constructor(opts) {
super(opts);
const { title, sandbox } = opts;
if (title) this.dom_element.title = title;
if (sandbox) this.dom_element.sandbox = sandbox;
}
}
```
---
## 版本历史
- **v1.0**:初始版本,包含 `Iframe``NewWindow` 基础实现及工厂注册。
---
> 📌 提示:更多关于 `bricks.Layout``bricks.JsWidget``bricks.Factory` 的细节,请参考主框架文档。

View File

@ -1,350 +0,0 @@
# `bricks.Image` 及相关图标组件技术文档
> 基于 `bricks.js` 框架的图像与图标类组件实现
---
## 概述
本模块定义了基于 `bricks.JsWidget` 的一系列图像与图标类组件,用于在网页中动态加载、显示和管理图像资源。主要包括:
- `bricks.Image`:基础图像组件
- `bricks.Icon`:可缩放的图标组件(继承自 `Image`
- `bricks.StatedIcon`:支持状态切换的图标组件(继承自 `Icon`
- `bricks.BlankIcon`:占位图标组件(无图像内容)
所有组件均通过 `bricks.Factory` 注册,可在配置系统中通过类型名实例化。
---
## 1. `bricks.Image`
### 类定义
```js
class extends bricks.JsWidget
```
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 配置选项对象 |
##### `opts` 属性
| 属性 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `url` | String | 否 | 图像的 URL 地址 |
| `default_url` | String | 否 | 当主 `url` 加载失败时使用的备用图像地址 |
#### 行为
- 调用父类构造函数。
- 若传入 `url`,则自动调用 `set_url()` 设置图像源。
---
### 方法
#### `create()`
创建一个 `<img>` 元素作为组件的 DOM 根节点。
```js
create()
```
> **说明**:该方法由框架调用,在初始化时创建 DOM 结构。
---
#### `set_url(url)`
设置图像的 `src` 属性,并绑定错误处理以支持默认图像。
```js
set_url(url)
```
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `url` | String | 图像资源 URL |
##### 行为
- 将 `this.url` 设为传入值。
- 如果配置了 `default_url`,则为 `<img>` 绑定 `onerror` 事件处理器,加载失败时自动切换到默认图。
- 设置 `dom_element.src = url`
---
#### `set_default_url()`
当图像加载失败时触发,切换至默认图像。
```js
set_default_url()
```
##### 行为
- 移除 `onerror` 监听器,防止重复触发。
- 将 `src` 设置为 `this.opts.default_url`
- 输出日志:`console.log('default_url', this.opts.default_url)`
---
#### `base64()`
将当前图像绘制到 Canvas 并导出为 Base64 编码的 PNG 数据 URL。
```js
base64()
```
##### 返回值
- `{String}` - 图像的 Data URL格式如`data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...`
##### 实现逻辑
1. 创建临时 `<canvas>` 元素。
2. 获取上下文并设置画布尺寸等于图像实际宽高。
3. 使用 `drawImage()` 将图像绘制到画布。
4. 调用 `toDataURL('image/png')` 生成 Base64 字符串。
5. 返回结果(未去除头部前缀)。
> ⚠️ 注意:此方法依赖图像已完全加载,否则可能绘制空白或异常内容。
---
#### `removeBase64Header(base64String)`
移除 Base64 字符串中的 MIME 头部信息(例如 `data:image/png;base64,`)。
```js
removeBase64Header(base64String)
```
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `base64String` | String | 完整的 Data URL 字符串 |
##### 返回值
- `{String}` - 纯 Base64 编码内容
##### 示例
```js
const clean = removeBase64Header('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==');
// → 'R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='
```
---
## 2. `bricks.Icon`
### 类定义
```js
class extends bricks.Image
```
扩展 `Image` 类,提供基于字符单位(`charsize`)的响应式尺寸控制。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
`bricks.Image`,额外支持以下选项:
##### `opts` 扩展属性
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `rate` | Number | `1` | 缩放倍率,相对于 `charsize` |
| `cwidth` | Number | `1` | 占据的字符宽度数 |
| `cheight` | Number | `1` | 占据的字符高度数 |
| `dynsize` | Boolean | `true` | 是否启用动态尺寸调整 |
#### 行为
- 调用父类构造函数。
- 自动执行 `options_parse()` 进行配置解析与尺寸设置。
---
### 方法
#### `options_parse()`
解析配置项并设置图标尺寸及图像源。
```js
options_parse()
```
##### 动作
1. 设置 `rate`(默认为 1
2. 计算样式尺寸:`siz = bricks.app.charsize * rate + 'px'`
3. 调用 `set_url(this.url)` 重新加载图像(确保尺寸生效后加载)
4. 设置 `cwidth`, `cheight`, `dynsize`
5. 调用 `charsize_sizing()` 应用尺寸策略
> ✅ 提示:`charsize_sizing()``JsWidget` 提供的方法,用于根据字符网格进行布局适配。
---
## 3. `bricks.StatedIcon`
### 类定义
```js
class extends bricks.Icon
```
支持多状态切换的图标组件,常用于表示不同 UI 状态(如开/关、在线/离线等)。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
| 属性 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `states` | Array<{state: String, url: String}> | 是 | 状态列表,每个状态包含名称和对应图像 URL |
| `state` | String | 否 | 初始状态名;若未指定,则使用第一个状态 |
#### 示例配置
```js
{
states: [
{ state: 'online', url: '/img/online.png' },
{ state: 'offline', url: '/img/offline.png' }
],
state: 'online'
}
```
#### 行为
- 调用父类构造函数。
- 自动执行 `options_parse()` 初始化状态。
---
### 方法
#### `options_parse()`
初始化状态机逻辑。
```js
options_parse()
```
##### 行为
- 若未定义 `states` 数组,则直接返回。
- 若未设置初始 `state`,则设为 `states[0].state`
- 调用 `set_state(this.state)` 激活初始状态
---
#### `set_state(state)`
切换当前图标状态,并更新图像。
```js
set_state(state)
```
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `state` | String | 要切换到的状态名称 |
##### 行为
- 更新 `this.state`
- 遍历 `this.states` 查找匹配项
- 找到后设置 `this.url = s.url`
- 调用 `super.options_parse()` 重新应用图像和尺寸
> 💡 支持链式调用与运行时状态变更。
---
## 4. `bricks.BlankIcon`
### 类定义
```js
class extends bricks.JsWidget
```
一个不渲染图像的“空白图标”,仅保留图标布局结构,用作占位符。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
`Icon`,支持:
- `rate`: 缩放比例
- `cwidth`, `cheight`: 字符单元占用
- `dynsize`: 是否动态调整大小
#### 行为
- 不创建图像元素。
- 仅调用 `charsize_sizing()` 实现布局占位。
---
## 工厂注册
所有组件均已注册至 `bricks.Factory`,可在模板或配置中通过字符串类型名实例化:
```js
bricks.Factory.register('Image', bricks.Image);
bricks.Factory.register('StatedIcon', bricks.StatedIcon);
bricks.Factory.register('Icon', bricks.Icon);
bricks.Factory.register('BlankIcon', bricks.BlankIcon);
```
### 使用示例(假设模板语法)
```json
{
"type": "Icon",
"url": "/icons/save.png",
"rate": 1.5,
"cwidth": 2,
"cheight": 2
}
```
---
## 总结
| 组件 | 用途 | 特性 |
|------|------|------|
| `Image` | 显示普通图像 | 支持错误 fallback、Base64 导出 |
| `Icon` | 可缩放图标 | 基于 `charsize` 自适应尺寸 |
| `StatedIcon` | 多状态图标 | 支持运行时状态切换 |
| `BlankIcon` | 布局占位符 | 无图像,仅占空间 |
---
## 注意事项
1. **图像跨域问题**:使用 `base64()` 方法时,若图像来自跨域服务器且未开启 CORSCanvas 将被污染导致无法导出数据。
2. **异步加载**`base64()` 应在图像 `load` 事件完成后调用,否则结果不可靠。
3. **内存泄漏风险**:频繁调用 `base64()` 会创建大量临时 Canvas建议缓存或节流。
4. **默认图像防循环**:确保 `default_url` 图像本身有效,避免 `onerror` 死循环。
---
## 版本信息
- 框架:`bricks.js`
- 模块:`image.js`
- 最后更新2025年4月5日
---
**建议使用场景**
- 图标按钮、状态指示器、用户头像、动态图像预览等需要灵活控制图像行为的 UI 元素。

View File

@ -1,58 +0,0 @@
# bricks
像搭积木一样的编写前端应用bricks希望提供开发者所需的底层显示控件开发应用时采用简单的组装和插拔方式完成应用的开发
使用bricks开发应用的好处
* 质量和性能可控开发者开发的应用质量和性能依赖bricks提供的提供底层控件。
* 简单的开发方式开发者以编写json数据文件的形式开发前端应用以后可以提供一个可视化开发平台
## Bricks控件描述文件
Bricks使用json格式描述控件具体格式要求请看[控件描述文件](descjson.md)
## BricksApp
Bricks应用类参见[BricksApp](bricksapp.md)
## bricks主要函数
### widgetBuild
创建控件函数,详细说明请看[这里](bricks.md)
### getWigetById
从DOM树查找控件详细说明请看[这里](bricks.md)
## bricks控件
Bricks的所有控件均继承自JsWidget控件间的继承关系请看[控件继承树](inherittree.md)
控件是bricks的基础每个控件实现了特定显示功能或布局能力开发者使用这些控件来构建应用
bricks的开发理念是应用开发可分为底层控件的开发以及操控底层控件来搭建应用。
通过这样分工,让精通底层开发的人员专心开发底层控件,而精通操控控件的人员专心搭建应用,从而提高开发效率和开发质量,希望大家参与进来一起做。
关于控件更多的信息,请看[控件](widgets.md)
## 依赖
如果要使用Markdown需要引用marked模块
如果用到图表, 需要引用echarts
* [Marked](https://github.com/markedjs/marked)
* [echarts](https://echarts.apache.org/zh/index.html)
## build
bricks 使用uglifyjs 压缩
在bricks目录下执行
```
./build.sh
```
命令执行完后在项目的dist目录下会生成bricks.js 和bricks.min.js并将examples和docs目录复制到本机到wwwroot目录
按照开发者本地web服务器的配置请修改build.sh的目标路径。
## 引用
```
<link rel="stylesheet" href="/bricks/css/bricks.css" />
<script src="/bricks/bricks.js"></script>
```
## 例子
配置好本地服务器后可以使用build.sh将bricks项目内容复制到本地网站后台之后在网站的/bricks/examples目录中查看bricks提供的例子程序

View File

@ -1,10 +0,0 @@
{
"widgettype":"MarkdownViewer",
"options":{
"width":"100%",
"height":"100%",
"navigator":true,
"md_url":"{{entire_url('README.md')}}"
}
}

View File

@ -1,66 +0,0 @@
# index.html文件内容
一个例子
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="http://localhost:9080/bricks/3parties/xterm.css" />
<link href="http://localhost:9080/bricks/3parties/video-js.css" rel="stylesheet" />
<link rel="stylesheet" href="http://localhost:9080/bricks/css/bricks.css">
<!--
在这里插入项目的css文件
-->
<!-- support IE8 (for Video.js versions prior to v7) -->
<script src="http://localhost:9080/bricks/3parties/videojs-ie8.min.js"></script>
</head>
<body>
<!--
在这里添加项目的script脚本
--->
<script src="http://localhost:9080/bricks/3parties/marked.min.js"></script>
<script src="http://localhost:9080/bricks/3parties/xterm.js"></script>
<script src="http://localhost:9080/bricks/3parties/video.min.js"></script>
<script src="http://localhost:9080/bricks/3parties/recorder.wav.min.js"></script>
<!--- <script src="http://localhost:9080/bricks/3parties/videojs-contrib-hls.min.js"></script> --->
<script src="http://localhost:9080/bricks/bricks.js"></script>
<script>
/*
if (bricks.is_mobile()){
alert('wh=' + window.innerWidth + ':' + window.innerHeight);
}
*/
const opts = {
/*
*/
"widget":
/*
以下是root控件的内容
*/
{
"widgettype":"MarkdownViewer",
"options":{
"width":"100%",
"height":"100%",
"navigator":true,
"md_url":"http://localhost:9080/bricks/docs/cn/README.md"
}
}
/*
root控件内容结束
*/
};
const app = new bricks.App(opts);
app.run();
</script>
</body>
</html>
```
说明

View File

@ -1,102 +0,0 @@
# Bricks控件继承树
```
JsWidget
|
--- AudioPlayer
|
--- Image
| |
| --- Icon
|
___ BlankIcon
|
___ Layout
| |
| --- VBox
| | |
| | --- Accordion
| | |
| | --- DataGrid
| | |
| | --- Form
| | |
| | --- MarkdownViewer
| | |
| | --- Menu
| | |
| | --- Message
| | | |
| | | --- Error
| | |
| | --- Popup
| | |
| | --- ScrollPanel
| | |
| | --- TreeNode
| | |
| | --- Tree
| | | |
| | | --- EditableTree
| | |
| | --- VideoPlayer
| |
| --- HBox
| | |
| | --- MiniForm
| |
| --- MultipleStateImage
| |
| --- Toolbar
| |
| --- TabPanel
| |
| --- Modal
| |
| --- HFiller
| |
| --- VFiller
| |
| --- Button
| |
| --- UiType
| | |
| | --- UiStr
| | | |
| | | --- UiPassword
| | | |
| | | --- UiInt
| | | | |
| | | | --- UiFloat
| | | |
| | | --- UiTel
| | | |
| | | --- UiEmail
| | | |
| | | --- UiFile
| | | |
| | | --- UiDate
| | | |
| | | --- UiAudio
| | | |
| | | --- UiVideo
| | |
| | --- UiCheck
| | |
| | --- UiCheckBox
| | |
| | --- UiText
| | |
| | --- UiCode
|
--- MdText
|
___ Video
|
--- TextBase
|
--- Text
|
--- Title1, Title2, Title3, Title4, Title5, Title6
```

View File

@ -1,621 +0,0 @@
# Bricks UI 组件技术文档
> **版本**: 1.0
> **作者**: Bricks Framework Team
> **描述**: `bricks.UiType` 及其子类构成了一套用于构建表单输入控件的组件体系,支持文本、密码、数字、日期、文件上传、选择框等多种输入类型。所有组件均继承自统一基类 `bricks.UiType`,并可通过工厂模式动态创建。
---
## 目录
- [概述](#概述)
- [核心基类](#核心基类)
- [`bricks.UiType`](#bricksuitype)
- [`bricks.UiHide`](#bricksuihide)
- [基础输入控件](#基础输入控件)
- [`bricks.UiStr`](#bricksuistr)
- [`bricks.UiPassword`](#bricksuipassword)
- [`bricks.UiInt`](#bricksuiint)
- [`bricks.UiFloat`](#bricksuifloat)
- [`bricks.UiTel`](#bricksuitel)
- [`bricks.UiEmail`](#bricksuiemail)
- [`bricks.UiDate`](#bricksuidate)
- [`bricks.UiText`](#bricksuitext)
- [`bricks.UiCode`](#bricksuicode)
- [复合与特殊控件](#复合与特殊控件)
- [`bricks.UiCheck`](#bricksuicheck)
- [`bricks.UiCheckBox`](#bricksuicheckbox)
- [`bricks.UiSearch`](#bricksuisearch)
- [多媒体输入控件](#多媒体输入控件)
- [`bricks.UiFile`](#bricksuifile)
- [`bricks.UiImage`](#bricksuiimage)
- [`bricks.UiAudio`](#bricksuiaudio)
- [`bricks.UiVideo`](#bricksuivideo)
- [工厂系统](#工厂系统)
- [`bricks._Input`](#bricks_input)
- [`Input` 工厂实例](#input-工厂实例)
---
## 概述
该模块提供了一个可扩展的 UI 输入组件框架,基于 JavaScript 类和 DOM 封装,实现以下特性:
- 统一接口:所有输入控件都实现了 `getValue()`, `setValue()`, `reset()`, `resultValue()` 等方法。
- 表单集成:支持通过 `set_formdata(formData)` 方法将值附加到 `FormData` 对象中。
- 值校验与格式化:通过正则表达式(`pattern`)进行输入过滤。
- 动态数据加载:如 `UiCode``UiCheckBox` 支持从 URL 异步加载选项数据。
- 用户交互事件:支持 `focus`, `blur`, `changed`, `keydown` 等事件绑定。
- 国际化支持:标签文本使用 `bricks.app.i18n._()` 进行翻译。
- 多媒体录制支持:图像、音频、视频可通过摄像头/麦克风直接采集。
---
## 核心基类
### `bricks.UiType`
**继承自**: `bricks.Layout`
**用途**: 所有输入控件的抽象基类,定义通用行为。
#### 构造函数
```js
new bricks.UiType(opts)
```
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.name` | String | 是 | 控件名称(对应表单字段名) |
| `opts.value` / `opts.defaultvalue` | Any | 否 | 初始值或默认值 |
| `opts.required` | Boolean | 否 | 是否为必填项 |
#### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `name` | String | 字段名称 |
| `value` | Any | 当前值 |
| `required` | Boolean | 是否必填 |
| `ctype` | String | 内部类型标识,默认 `'text'` |
| `dom_element` | HTMLElement | 渲染后的 DOM 元素 |
#### 方法
| 方法 | 说明 |
|------|------|
| `getValue()` → Object | 返回 `{ name: value }`,若存在用户数据则合并返回 |
| `set_formdata(formData)` | 将当前值添加到 `FormData` 中 |
| `resultValue()` → Any | 获取实际提交值(子类重写) |
| `focus()` | 聚焦输入元素 |
| `reset()` | 重置为初始值 |
| `setValue(v)` | 设置值并更新 DOM |
| `set_disabled(f)` | 设置禁用状态 |
| `set_readonly(f)` | 设置只读状态 |
| `set_required(f)` | 设置是否必填 |
---
### `bricks.UiHide`
**继承自**: `bricks.UiType`
**用途**: 隐藏字段,不显示但可携带数据。
#### 特性
- `uitype = 'hide'`
- 自动设置 `display: none` 样式
#### 示例
```js
new bricks.UiHide({ name: 'user_id', value: '12345' })
```
---
## 基础输入控件
### `bricks.UiStr`
**继承自**: `bricks.UiType`
**用途**: 单行文本输入框。
#### 支持配置项 (`opts`)
| 属性 | 类型 | 说明 |
|------|------|------|
| `align` | String | 文本对齐方式:`left`, `center`, `right` |
| `length` | Number | 最大字符数maxlength |
| `minlength` | Number | 最小字符数 |
| `tip` | String | 提示信息(暂未使用) |
| `width` | String | 宽度CSS 单位) |
| `readonly` | Boolean/String | 是否只读 |
| `required` | Boolean | 是否必填 |
| `placeholder` | String | 占位符文本(自动国际化) |
| `css` | String | 自定义 CSS 类名 |
| `dynsize` | Boolean | 是否启用动态字体适配 |
| `cfontsize` | Number | 字体缩放比例 |
#### 方法扩展
- `create()`:创建 `<input type="text">`
- `check_pattern(value)`:用正则校验输入
- `set_value_from_input(event)`:输入时触发值更新与事件派发
- `onfocus/onblur`:焦点处理,添加高亮样式
#### 示例
```js
new bricks.UiStr({
name: 'username',
placeholder: 'Enter your username',
required: true,
width: '200px'
})
```
---
### `bricks.UiPassword`
**继承自**: `bricks.UiStr`
**用途**: 密码输入框。
#### 特性
- `type="password"`
- `uitype='password'`
#### 示例
```js
new bricks.UiPassword({ name: 'password', placeholder: '******' })
```
---
### `bricks.UiInt`
**继承自**: `bricks.UiStr`
**用途**: 整数输入。
#### 特性
- `type="number"`
- `textAlign: right`
- 正则匹配:`\d*`
- `resultValue()` 返回 `parseInt(value)`
#### 示例
```js
new bricks.UiInt({ name: 'age', length: 3 })
```
---
### `bricks.UiFloat`
**继承自**: `bricks.UiInt`
**用途**: 浮点数输入。
#### 特性
- 正则匹配:`\d*\.?\d+`
- 支持小数位控制(`dec_len`
- 自动设置 `step` 属性以限制精度
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `dec_len` | 2 | 小数位数 |
#### 示例
```js
new bricks.UiFloat({ name: 'price', dec_len: 2 })
```
---
### `bricks.UiTel`
**继承自**: `bricks.UiStr`
**用途**: 电话号码输入。
#### 特性
- `type="tel"`
- 默认正则:`[+]?\\d+`
- 支持自定义 `pattern`
#### 示例
```js
new bricks.UiTel({ name: 'phone', pattern: '[0-9]{11}' })
```
---
### `bricks.UiEmail`
**继承自**: `bricks.UiStr`
**用途**: 邮箱地址输入。
#### 特性
- `type="email"`
- 可选自定义 `pattern`
#### 示例
```js
new bricks.UiEmail({ name: 'email', required: true })
```
---
### `bricks.UiDate`
**继承自**: `bricks.UiStr`
**用途**: 日期选择器。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `max_date` | 最大允许日期(字符串格式 YYYY-MM-DD |
| `min_date` | 最小允许日期 |
#### 特性
- `type="date"`
- 若值为空,`resultValue()` 返回 `null`
#### 示例
```js
new bricks.UiDate({
name: 'birth_date',
min_date: '1900-01-01',
max_date: '2024-12-31'
})
```
---
### `bricks.UiText`
**继承自**: `bricks.UiType`
**用途**: 多行文本域textarea
#### 支持配置
| 属性 | 类型 | 说明 |
|------|------|------|
| `rows` | Number | 行数 |
| `cols` | Number | 列数 |
| `height` | String | 高度(默认 `'200px'` |
#### 特性
- 支持 Tab 缩进(每 4 空格)
- Shift+Tab 删除缩进
- Enter 换行(阻止默认行为)
- 支持代码编辑风格缩进逻辑
#### 方法
| 方法 | 说明 |
|------|------|
| `handle_tab_indent(erase)` | 处理 Tab 缩进或反向删除 |
| `handle_enter()` | 插入换行并保持光标位置 |
#### 示例
```js
new bricks.UiText({ name: 'description', rows: 6, cols: 50 })
```
---
### `bricks.UiCode`
**继承自**: `bricks.UiType`
**用途**: 下拉选择框(类似 `<select>`),常用于枚举字段。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `data` | 本地数据数组 `[ { value: '', text: '' } ]` |
| `dataurl` | 数据源 URL异步加载 |
| `params` | 请求参数 |
| `method` | HTTP 方法GET/POST |
| `valueField` | 值字段名(默认 `'value'` |
| `textField` | 显示文本字段名(默认 `'text'` |
| `nullable` | 是否允许空选项 |
#### 特性
- 支持动态加载数据(`load_data()`
- 支持级联刷新(通过 `get_data(event)` 接收上级事件参数)
- 自动生成 `<option>` 元素
- 支持国际化文本显示
#### 方法
| 方法 | 说明 |
|------|------|
| `build_options(data)` | 构建下拉选项 |
| `load_data(params)` | 异步获取数据 |
| `get_data(event?)` | 触发数据加载(可带参数) |
#### 示例
```js
new bricks.UiCode({
name: 'status',
dataurl: '/api/statuses',
textField: 'label',
valueField: 'id'
})
```
---
## 复合与特殊控件
### `bricks.UiCheck`
**继承自**: `bricks.UiType`
**用途**: 单个复选图标SVG 图标切换)。
#### 特性
- 使用 `MultipleStateIcon` 显示 checked/unchecked 状态
- 图标资源路径:
- checked: `imgs/checkbox-checked.svg`
- unchecked: `imgs/checkbox-unchecked.svg`
- 值为布尔类型
#### 方法
| 方法 | 说明 |
|------|------|
| `setValue(true/false)` | 设置状态 |
| `set_value_from_input()` | 响应图标点击,派发 `changed` 事件 |
#### 示例
```js
new bricks.UiCheck({ name: 'agree', value: false })
```
---
### `bricks.UiCheckBox`
**继承自**: `bricks.UiType`
**用途**: 多选或单选组fieldset + legend + 多个 UiCheck
#### 支持配置
| 属性 | 说明 |
|------|------|
| `data` | 选项列表 `[ { value: '', text: '' } ]` |
| `dataurl` | 异步数据源 |
| `textField` | 显示字段 |
| `valueField` | 值字段 |
| `label` | 分组标题legend |
#### 特性
- 支持多选(数组)和单选(字符串)
- 动态构建多个 `UiCheck` 实例
- 支持异步加载数据(`load_data_onfly()`
#### 方法
| 方法 | 说明 |
|------|------|
| `build_checkboxs()` | 根据数据生成复选框列表 |
| `set_value_from_input()` | 更新选中值并广播 `changed` 事件 |
| `setValue(v)` | 设置选中项(支持数组或单值) |
#### 示例
```js
new bricks.UiCheckBox({
name: 'hobbies',
label: 'Your Hobbies',
data: [
{ value: 'reading', text: 'Reading' },
{ value: 'music', text: 'Music' }
],
multicheck: true
})
```
---
### `bricks.UiSearch`
**继承自**: `bricks.HBox`
**用途**: 搜索选择器,点击图标弹出窗口选择数据。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `search_url` | 弹窗内容页面 URL |
| `valueField` | 返回值字段 |
| `textField` | 显示字段 |
| `popup_options` | 弹窗配置 |
| `text` | 显示文本(非值) |
#### 特性
- 包含一个只读 `UiStr` 和一个搜索图标SVG
- 点击图标打开弹窗(`default_popupwindow`
- 弹窗内查找 `Tabular` 组件并绑定事件
- 选择后调用 `set_data()` 更新值
#### 方法
| 方法 | 说明 |
|------|------|
| `open_search_window()` | 打开搜索弹窗 |
| `set_data(event)` | 接收选择结果并填充 |
#### 示例
```js
new bricks.UiSearch({
name: 'customer_id',
search_url: '/pages/customer_picker.html',
valueField: 'id',
textField: 'name'
})
```
---
## 多媒体输入控件
### `bricks.UiFile`
**继承自**: `bricks.VBox`
**用途**: 文件上传控件,支持拖拽和点击选择。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `accept` | MIME 类型过滤(如 `'image/'`, `'audio/'` |
| `multiple` | 是否允许多选 |
#### 特性
- 支持拖拽上传
- 自动监听 `dragover`, `drop` 等事件
- 内部包含隐藏 `<input type="file">`
- 支持 `set_input_file(files)` 手动设置文件列表
#### 方法
| 方法 | 说明 |
|------|------|
| `handleFileSelect()` | 处理文件选择 |
| `dropHandle()` | 处理拖放事件 |
| `resultValue()` | 返回 File 或 FileList |
#### 示例
```js
new bricks.UiFile({ name: 'attachment', accept: 'application/pdf', multiple: true })
```
---
### `bricks.UiImage`
**继承自**: `bricks.UiFile`
**用途**: 图像上传 + 拍照功能。
#### 特性
- `accept='image/'`
- 添加拍照按钮(调用 `SysCamera`
- 支持预览上传或拍摄的图片(`show_image()`
#### 方法
| 方法 | 说明 |
|------|------|
| `take_photo()` | 打开相机 |
| `accept_photo()` | 接收拍摄结果 |
| `_show_image(file)` | 显示图片预览URL 或 Blob |
#### 示例
```js
new bricks.UiImage({ name: 'avatar' })
```
---
### `bricks.UiAudio`
**继承自**: `bricks.UiFile`
**用途**: 音频上传 + 录音功能。
#### 特性
- `accept='audio/'`
- 添加录音按钮(调用 `SysAudioRecorder`
- 支持播放预览(`AudioPlayer`
#### 方法
| 方法 | 说明 |
|------|------|
| `open_recorder()` | 打开录音器 |
| `accept_audio()` | 接收录音文件 |
| `_show_audio(file)` | 显示音频播放器 |
#### 示例
```js
new bricks.UiAudio({ name: 'voice_note' })
```
---
### `bricks.UiVideo`
**继承自**: `bricks.UiFile`
**用途**: 视频上传 + 录制功能。
#### 特性
- `accept='video/'`
- 添加录像按钮(调用 `SysVideoRecorder`
- 支持视频预览(`VideoPlayer`
#### 方法
| 方法 | 说明 |
|------|------|
| `open_recorder()` | 打开录像器 |
| `accept_video()` | 接收录像文件 |
| `_show_video(file)` | 显示视频播放器 |
#### 示例
```js
new bricks.UiVideo({ name: 'demo_video' })
```
---
## 工厂系统
### `bricks._Input`
**用途**: 输入控件工厂类,用于注册和创建 UI 组件。
#### 方法
| 方法 | 说明 |
|------|------|
| `register(name, uitype, Klass)` | 注册组件类 |
| `factory(options)` | 根据 `uitype` 创建实例 |
#### 示例
```js
const factory = new bricks._Input();
factory.register('UiStr', 'str', bricks.UiStr);
const input = factory.factory({ uitype: 'str', name: 'title' });
```
---
### `Input` 工厂实例
全局已初始化的工厂对象,预注册了所有标准控件。
#### 已注册映射表
| 名称 (name) | uitype | 类 |
|------------|--------|-----|
| `UiStr` | `str` | `bricks.UiStr` |
| `UiPassword` | `password` | `bricks.UiPassword` |
| `UiInt` | `int` | `bricks.UiInt` |
| `UiFloat` | `float` | `bricks.UiFloat` |
| `UiTel` | `tel` | `bricks.UiTel` |
| `UiEmail` | `email` | `bricks.UiEmail` |
| `UiDate` | `date` | `bricks.UiDate` |
| `UiText` | `text` | `bricks.UiText` |
| `UiCode` | `code` | `bricks.UiCode` |
| `UiCheck` | `check` | `bricks.UiCheck` |
| `UiCheckBox` | `checkbox` | `bricks.UiCheckBox` |
| `UiFile` | `file` | `bricks.UiFile` |
| `UiImage` | `image` | `bricks.UiImage` |
| `UiAudio` | `audio`, `audiorecorder` | `bricks.UiAudio` |
| `UiVideo` | `video` | `bricks.UiVideo` |
| `UiSearch` | `search` | `bricks.UiSearch` |
| `UiHide` | `hide` | `bricks.UiHide` |
#### 使用方式
```js
const field = Input.factory({ uitype: 'email', name: 'contact_email' });
```
---
## 总结
本组件库提供了完整且可扩展的前端表单输入解决方案,具备以下优势:
✅ 统一 API 接口
✅ 支持异步数据加载
✅ 多媒体采集支持
✅ 可定制样式与行为
✅ 支持国际化
✅ 事件驱动架构
适用于构建复杂的表单页面、数据录入系统、移动端 Web 应用等场景。
---
> ✅ **提示**:建议结合 `bricks.Layout``bricks.HBox`/`VBox` 布局容器组织表单结构。
> 📁 **资源路径**:图标资源位于 `imgs/` 目录下,需确保正确部署。

View File

@ -1,37 +0,0 @@
# 安装bricks
## 下载bricks
执行以下命令
```
git clone https://git.kaiyauncloud.cn/yumoqing/bricks
```
执行完成后bricks的源码存放在bricks目录中
## 构建和安装
执行以下命令
```
cd bricks
cd bricks
./build.sh
cd ..
ln -s `pwd`/dist $(web根目录}/bricks
```
命令执行完成后构建完成的目标文件在bricks/dist目录中
并将bricks/dist目录复制或链接到web服务器的根文件目录中
## 依赖
bricks框架目前引用如下软件包
* marked.min.js Markdown解析和展示包
* recorder.wav.min.js 跨平台的录音软件包
* video.min.js 跨平台的视频播放软件包
* videojs-ie8.min.js video视频播放器ie8兼容包
* xterm.js web终端软件包
bricks把依赖的第三方软件包放在/bricks/3parties目录下

View File

@ -1,391 +0,0 @@
# Bricks HTTP 模块技术文档
本模块为 `bricks` 前端框架提供了一套完整的 HTTP 请求处理工具集支持多种响应类型文本、JSON、二进制流等并封装了自动参数注入、错误处理、会话管理与登录重定向等功能。
---
## 目录
- [概述](#概述)
- [核心功能](#核心功能)
- [公共函数](#公共函数)
- [基类:`HttpText`](#基类httptext)
- [派生类](#派生类)
- [`HttpArrayBuffer`](#httparraybuffer)
- [`HttpBin`](#httpbin)
- [`HttpResponse`](#httpresponse)
- [`HttpResponseStream`](#httpresponsestream)
- [`HttpRaw`](#httpraw)
- [`HttpJson`](#httpjson)
- [快捷方法](#快捷方法)
- [使用示例](#使用示例)
---
## 概述
`bricks.Http*` 系列类基于 `fetch` API 构建,旨在统一前端网络请求行为。主要特性包括:
- 自动附加设备信息和会话参数
- 支持 GET/POST 请求及 FormData 或 JSON 数据格式
- 统一的错误提示机制401、403、其他错误
- 可扩展的响应处理器文本、JSON、流式解析等
- 登录拦截与跳转支持(待完善)
该模块适用于 Web 应用中需要与后端服务交互的所有场景。
---
## 核心功能
| 功能 | 描述 |
|------|------|
| 参数合并 | 自动注入 `_webbricks_`, 屏幕宽高, 移动端标识等参数 |
| 会话管理 | 从响应头读取 `Set-Cookie` 并保存会话;发送时自动携带 |
| 错误处理 | 对 401、403 和非 OK 状态码进行统一 UI 提示 |
| 登录重定向 | 当返回 401 且存在 `login_url` 时触发登录流程(目前未完全实现) |
| 流式响应处理 | 支持逐行处理 chunked 响应数据(如 Server-Sent Events |
---
## 公共函数
### `url_params(data)`
将对象转换为 URL 查询字符串。
#### 参数
- `data`: `{[key: string]: any}` — 要编码的对象
#### 返回值
- `string` — 格式化后的查询字符串(例如 `"a=1&b=2"`
#### 示例
```js
url_params({a: 1, b: 'hello'}) // → "a=1&b=hello"
```
> ⚠️ 所有值都会通过 `encodeURIComponent` 编码。
---
## 基类:`HttpText`
所有 HTTP 客户端类的基类。
### 构造函数 `constructor(headers?)`
#### 参数
- `headers?`: `{[key: string]: string}` — 自定义请求头,默认为 `{ Accept: "text/html" }`
#### 行为说明
- 若未传入 `headers`,默认使用:
```js
{ "Accept": "text/html" }
```
- 使用 `bricks.extend()` 合并默认头与用户头。
- 自动收集以下上下文参数作为请求参数:
- `_webbricks_`: 固定值 `1`
- `width`: 屏幕宽度(来自 `bricks.app.screenWidth()`
- `height`: 屏幕高度(来自 `bricks.app.screenHeight()`
- `_is_mobile`: 是否为移动端(`'1'``'0'`
> ✅ 这些参数会在每次请求中自动附加。
### 方法
#### `url_parse(url)`
解析 URL 中的查询参数,并将其合并到内部 `this.params` 中。
##### 参数
- `url`: `string` — 原始 URL含 query
##### 返回值
- `string` — 清除 query 的基础 URL
##### 示例
```js
const client = new bricks.HttpText();
client.url_parse("/api/data?a=1&b=2");
// 结果client.params 包含 a=1, b=2
// 返回 "/api/data"
```
#### `add_own_params(params)`
合并用户参数与内部参数(包括 session
##### 参数
- `params`: `Object | FormData | null`
##### 返回值
- `Object | FormData` — 合并后的参数对象
##### 行为逻辑
- 如果是 `FormData`,直接追加键值对
- 否则创建新对象,合并 `this.params``params`
- 若存在 `bricks.app.get_session()`,额外添加 `session` 字段
#### `add_own_headers(headers)`
合并自定义请求头与实例默认头。
##### 参数
- `headers`: `{[key: string]: string} | null`
##### 返回值
- `Object` — 合并后的 headers
> 使用 `Object.assign(this.headers, headers)` 实现浅合并。
#### `async bricks_fetch(url, {method, headers, params})`
底层 `fetch` 封装,负责构建最终请求选项。
##### 参数
- `url`: `string` — 请求地址
- `method`: `'GET'|'POST'|'HEAD'|...` — HTTP 方法(默认 `'GET'`
- `headers`: `Object` — 请求头
- `params`: `Object|FormData` — 请求参数
##### 处理逻辑
| 条件 | 行为 |
|------|------|
| `params instanceof FormData` | 强制设为 POST`body = params` |
| `GET/HEAD` 请求 | 将 `params` 转为 query string 拼接到 URL |
| 其他方法(如 POST | `body = JSON.stringify(params)` |
##### 返回值
- `Response` — fetch 返回的原生响应对象
#### `async httpcall(url, opts)`
高层封装,包含状态码判断与错误处理。
##### 参数
- `url`: `string`
- `opts`: `{method, headers, params}`
##### 流程
1. 调用 `bricks_fetch` 发起请求
2. 检查响应状态:
- **401** 且配置了 `bricks.app.login_url` → 触发 `withLoginInfo`
- **403** → 显示“禁止访问”弹窗
- 非 `ok` → 显示通用错误弹窗
3. 成功则调用 `get_result_data()` 获取结果
4. 解析 `Set-Cookie` 并调用 `bricks.app.save_session(session)` 保存会话
##### 返回值
- `string | null` — 成功返回响应内容,失败返回 `null`
#### `async withLoginInfo(url, params)`
处理 401 时尝试重新登录(当前仅为占位实现)。
> ❗ 当前仅创建一个 `urlwidget` 加载登录页,未完成回调或重试逻辑。
#### `async get_result_data(resp)`
提取响应体数据的方法(可被子类覆盖)。
##### 默认实现(`HttpText`
```js
return await resp.text();
```
---
## 派生类
### `HttpArrayBuffer`
继承自 `HttpText`,用于下载二进制文件(如字体、模型等)。
#### 重写方法
```js
async get_result_data(resp) {
return await resp.arrayBuffer();
}
```
---
### `HttpBin`
继承自 `HttpText`,以 Blob 形式获取响应(适合图片、音频等)。
#### 重写方法
```js
async get_result_data(resp) {
return await resp.blob();
}
```
---
### `HttpResponse`
继承自 `HttpText`,直接返回原始 `Response` 对象。
#### 重写方法
```js
async get_result_data(resp) {
return resp;
}
```
> ✅ 适用于需手动处理 header 或 stream 的高级场景。
---
### `HttpResponseStream`
继承自 `HttpResponse`,专用于处理流式响应(如 SSE
#### 方法:`async handle_chunk(resp, handler)`
逐行解析 chunked 文本流,并调用 `handler(line)`
##### 参数
- `resp`: `Response` — fetch 返回的响应对象
- `handler`: `(line: string) => void` — 每一行数据的处理器
##### 实现细节
- 使用 `ReadableStream.getReader()` + `TextDecoder`
- 按 `\n` 分割缓冲区,确保跨 chunk 边界正确切分
- 最终剩余内容也会传递给 `handler`
##### 示例用法
```js
const streamClient = new bricks.HttpResponseStream();
const resp = await streamClient.get('/stream-endpoint');
await streamClient.handle_chunk(resp, (line) => {
console.log('Received:', line);
});
```
---
### `HttpRaw`
`HttpBin` 相同,返回 `Blob`
> ⚠️ 注释中可能表示未来用途不同,但当前实现与 `HttpBin` 完全一致。
---
### `HttpJson`
专用于 JSON 接口通信,设置默认 Accept 头并解析 JSON 响应。
#### 构造函数增强
```js
this.headers = { "Accept": "application/json" };
bricks.extend(this.headers, headers); // 用户头可覆盖
```
#### 重写方法
```js
async get_result_data(resp) {
return await resp.json();
}
```
> ✅ 推荐用于 RESTful API 调用。
---
## 快捷方法
模块末尾导出多个常用快捷函数,绑定到全局命名空间:
| 变量名 | 类型 | 说明 |
|--------|------|------|
| `bricks.hc` | `new HttpText()` | 文本客户端实例 |
| `bricks.tget` | `Function` | `hc.get` 的绑定方法GET 文本) |
| `bricks.tpost` | `Function` | `hc.post` 的绑定方法POST 文本) |
| `bricks.jc` | `new HttpJson()` | JSON 客户端实例 |
| `bricks.jcall` | `Function` | `jc.httpcall` 的绑定方法(调用 JSON 接口) |
| `bricks.jget` | `Function` | `jc.get` 的绑定方法GET JSON |
| `bricks.jpost` | `Function` | `jc.post` 的绑定方法POST JSON |
### 示例
```js
// 获取 HTML 内容
const html = await bricks.tget('/page', { params: { id: 123 } });
// 调用 JSON 接口
const data = await bricks.jpost('/api/user', {
headers: { 'X-Token': 'abc' },
params: { name: 'Alice' }
});
```
---
## 使用示例
### 1. 发送普通 GET 请求
```js
const client = new bricks.HttpText();
const result = await client.get('/api/hello', {
params: { q: 'test' }
});
console.log(result); // 输出服务器返回的文本
```
### 2. 发送 JSON POST 请求
```js
const jsonClient = new bricks.HttpJson();
const user = await jsonClient.post('/api/users', {
params: { name: 'Bob', age: 30 }
});
// 自动设置 Content-Type 和 Accept并解析 JSON
```
### 3. 下载文件为 ArrayBuffer
```js
const bufferClient = new bricks.HttpArrayBuffer();
const resp = await bufferClient.get('/model.bin');
const arrayBuf = await resp; // 已是 arrayBuffer
```
### 4. 处理实时日志流
```js
const streamClient = new bricks.HttpResponseStream();
const resp = await streamClient.get('/logs/stream');
await streamClient.handle_chunk(resp, (line) => {
console.log('[LOG]', line);
});
```
---
## 注意事项
- 依赖 `bricks.app` 提供 `screenWidth()`, `screenHeight()`, `get_session()`, `save_session()` 等方法
- 依赖 `bricks.extend(obj, src)` 实现对象合并(类似 jQuery.extend
- `objget(headers, 'Set-Cookie')` 用于安全获取响应头(需外部定义)
- 登录流程尚未完整实现,`withLoginInfo` 仅为原型
---
## 待优化建议
| 问题 | 建议改进 |
|------|---------|
| `bufffer` 拼写错误 | 修复 `if (buffer != ''){ yield bufffer; }` 中的拼写 |
| `withLoginInfo` 无实际登录能力 | 应支持模态框输入并回调重试请求 |
| `handle_chunk` 正则 `/\\r?\\n/gm` 不必要 | 可简化为 `.split('\n')` |
| `bricks_fetch``method = 'POST'` 修改外层变量风险 | 应使用局部变量 |
---
## 版本信息
- 创建时间:未知
- 框架版本Bricks Framework v1.x+
- 作者Bricks Team
> 文档最后更新2025年4月5日

View File

@ -1,25 +0,0 @@
# JsWidget
Bricks框架的最原始的控件所有Bricks的控件均继承自JsWidget或其后代。
## 构建选项
## 属性
### dom_element
dom_element是控件对应的dom元素。
## 方法
### create()
创建dom元素的方法并将新创建的元素保存在dom_element属性中JsWidget创建一个”div“的元素 子类通过提供自己的create函数创建自己特定的dom元素。
### set_css(css_name, delflg)
增加或删除一个css类 当delflg为真时删除一个“css_name”css类否则增加一个“css_name”类
### sizable
sizable函数让当前类有按照bricks_app.charsize的大小设置自身大小的能力并在charsize变化时改变自身的大小
### change_fontsizets
change_fontsize函数由bricks_app.textsize_bigger()textsize_smaller函数调用用来改变控件的大小
## 事件

View File

@ -1,126 +0,0 @@
# `bricks.KeyPress` 组件技术文档
> 一个用于监听键盘按键并实时显示按键信息的 UI 组件。
---
## 概述
`bricks.KeyPress` 是基于 `bricks.VBox` 的扩展类,用于创建一个能够监听全局键盘事件(`keydown`)的组件。当用户按下任意键时,该组件会清除当前内容,并显示被按下的键名。
此组件常用于调试、快捷键提示或交互式输入反馈场景。
---
## 继承关系
- **继承自**: `bricks.VBox`
- **类型**: 类Class
- **注册名称**: `'KeyPress'`(通过 `bricks.Factory` 注册)
---
## 构造函数
```js
constructor(opts)
```
### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `opts` | Object | 传递给父类 `VBox` 的配置选项,如布局、样式等。 |
### 行为
1. 调用 `super(opts)` 初始化父类 `VBox`
2. 使用 `bricks.app.bind('keydown', ...)` 绑定全局键盘按下事件,将 `this.key_handler` 作为回调函数,并确保其上下文指向当前实例。
---
## 方法
### `key_handler(event)`
处理键盘按下事件的回调函数。
#### 参数
| 参数名 | 类型 | 说明 |
|---------|-------------|------------------------|
| `event` | KeyboardEvent | 浏览器原生键盘事件对象。 |
#### 实现逻辑
1. 获取 `event.key` 字符串表示(例如 `"a"`, `"Enter"`, `"Shift"` 等)。
2. 如果 `event.key` 不存在,则直接返回,不进行后续操作。
3. 调用 `this.clear_widgets()` 清除当前容器内的所有子控件。
4. 创建一个新的 `bricks.Text` 控件,显示文本:`"key press is: <key>"`
5. 使用 `this.add_widget(w)` 将新文本控件添加到当前容器中。
#### 示例输出
- 按下字母 A → 显示:`key press is: a`
- 按下回车键 → 显示:`key press is: Enter`
- 按下空格键 → 显示:`key press is: `(后面是一个空格)
---
## 工厂注册
```js
bricks.Factory.register('KeyPress', bricks.KeyPress);
```
注册后可通过工厂方法动态创建该组件:
```js
var keyPressWidget = bricks.Factory.create('KeyPress', { /* opts */ });
```
---
## 使用示例
```js
// 创建 KeyPress 组件实例
var keyDisplay = new bricks.KeyPress({
style: {
padding: '10px',
border: '1px solid #ccc'
}
});
// 添加到页面某个容器中
someContainer.add_widget(keyDisplay);
```
---
## 注意事项
- 依赖全局对象 `bricks.app` 必须实现 `.bind(eventType, handler)` 方法来绑定 DOM 事件。
- 仅响应 `keydown` 事件,不处理 `keyup``keypress`
- 每次按键都会清空原有内容,仅保留最新一次按键信息。
---
## 依赖项
- `bricks.VBox` —— 布局容器基类。
- `bricks.Text` —— 文本显示组件。
- `bricks.Factory` —— 组件工厂,用于注册和创建组件。
- `bricks.app` —— 应用级事件管理器。
---
## 版本信息
- **模块**: `bricks`
- **组件**: `KeyPress`
- **最后更新**: 请根据实际项目填写
---
✅ *可用于开发调试面板、快捷键教学模块等场景。*

View File

@ -1,359 +0,0 @@
# `bricks.Layout` 及其子类技术文档
本技术文档详细描述了 `bricks.Layout` 类及其相关布局组件(如 `VBox`, `HBox`, `Filler`, `ResponsableBox` 等)的功能、结构和使用方式。这些类是基于 `bricks.JsWidget` 构建的 UI 容器组件,支持键盘导航、动态内容管理与响应式布局。
---
## 目录
- [概述](#概述)
- [核心模块](#核心模块)
- [`bricks.Layout`](#brickslayout)
- [`bricks.VBox`](#bricksvbox)
- [`bricks.HBox`](#brickshbox)
- [`bricks.FHBox`](#bricksfhbox)
- [`bricks.FVBox`](#bricksfvbox)
- [`bricks.Filler`](#bricksfiller)
- [`bricks.ResponsableBox`](#bricksresponsablebox)
- [键盘选择机制](#键盘选择机制)
- [事件处理](#事件处理)
- [方法说明](#方法说明)
- [工厂注册](#工厂注册)
- [示例用法](#示例用法)
---
## 概述
`bricks` 是一个前端 UI 组件框架,`Layout` 是其基础容器类用于组织和管理子控件widgets。所有布局类均继承自 `bricks.Layout`,并可通过 CSS 类实现水平或垂直排列。此外,该系统支持**键盘导航**,允许用户通过方向键在可聚焦元素之间切换,并通过 Enter 键触发操作。
主要特性包括:
- 支持嵌套布局结构
- 提供标题、描述、工具栏构建方法
- 内置键盘导航支持(上下左右 + Enter
- 基于栈的层级导航机制
- 动态添加/删除子控件
- 响应式设计支持(`ResponsableBox`
---
## 核心模块
### `bricks.Layout`
#### 继承关系
```js
class Layout extends bricks.JsWidget
```
#### 属性
| 属性名 | 类型 | 描述 |
|-------|------|------|
| `_container` | Boolean | 标记为容器组件 |
| `keyselectable` | Boolean | 是否启用键盘选择功能 |
| `children` | Array | 子控件列表 |
| `selected_item` | Widget | 当前选中的子控件 |
| `title` | String (可选) | 容器标题文本 |
| `description` | String (可选) | 描述文本 |
| `toolbar` | Object (可选) | 工具栏配置对象 |
| `use_key_select` | Boolean (可选) | 初始化时是否自动启用键盘选择 |
> ⚠️ 注意:`use_key_select` 在代码中未明确定义于构造函数参数,但被条件判断引用,建议作为选项传入。
#### 构造函数
```js
constructor(options)
```
- **参数**
- `options` `{Object}` 配置项,支持以下字段:
- `title`: 显示标题
- `description`: 显示描述
- `toolbar`: 工具栏配置
- `keyselectable`: 是否允许键盘选择(默认 `false`
- `use_key_select`: 若为真,则自动调用 `enable_key_select()`
- **行为**
- 调用父类构造函数
- 初始化内部状态
- 若设置了 `use_key_select`,则立即启用键盘选择模式
---
### `bricks.VBox`
垂直布局容器,子元素垂直堆叠。
```js
class VBox extends Layout
```
- **构造函数**
- 自动设置 CSS 类:`vcontainer`
- **用途**:创建纵向排列的内容区域。
---
### `bricks.HBox`
水平布局容器,子元素水平排列。
```js
class HBox extends Layout
```
- **构造函数**
- 设置 CSS 类:`hcontainer`
- **用途**:横向排布控件。
---
### `bricks.FHBox`
固定行水平盒子(不换行)。
```js
class FHBox extends HBox
```
- **构造函数**
- 调用 `super(options)`
- 设置样式:`flexWrap: 'nowrap'`
- **用途**:确保子元素始终在同一行显示。
---
### `bricks.FVBox`
固定列垂直盒子(不换行)。
```js
class FVBox extends VBox
```
- **构造函数**
- 设置样式:`flexWrap: 'nowrap'`
- **用途**:防止垂直方向上的内容换列(较少见,通常用于嵌套场景)。
---
### `bricks.Filler`
弹性填充占位符,常用于 Flex 布局中占据剩余空间。
```js
class Filler extends Layout
```
- **构造函数**
- 设置 CSS 类:`filler`
- 注释中包含潜在增强样式(如 `flexGrow: 1`),可用于扩展
- **用途**:在布局中插入空白区域以平衡界面。
---
### `bricks.ResponsableBox`
响应式布局容器,根据宽高比动态调整布局方向。
```js
class ResponsableBox extends Layout
```
- **事件绑定**
- 监听 `'element_resize'` 事件,自动调用 `reset_type()`
- **`reset_type(event)` 行为**
- 判断当前容器宽度与高度:
- 若 `width > height` → 视为横向,应用 `hcontainer` 并设 `height: 100%`
- 否则 → 视为纵向,应用 `vcontainer` 并设 `width: 100%`
- 输出调试日志到控制台
> 💡 此类适合移动端或窗口缩放频繁的场景实现“自适应”UI 排列。
---
## 键盘选择机制
### 全局栈管理
```js
bricks.key_selectable_stack = [];
```
- 所有启用了键盘选择的 `Layout` 实例按进入顺序压入栈。
- 最顶层的实例才响应按键事件(通过 `is_currkeyselectable()` 判断)。
### 关键方法
| 方法 | 说明 |
|------|------|
| `enable_key_select()` | 启用键盘选择,注册事件监听,推入栈 |
| `disable_key_select()` | 停止监听,从栈顶弹出自身(若为当前层) |
| `is_currkeyselectable()` | 判断自己是否处于栈顶且可选 |
### 导航逻辑
| 键盘按键 | 功能 |
|--------|------|
| `ArrowDown` | 选择下一个子项(循环) |
| `ArrowUp` | 选择上一个子项(循环) |
| `ArrowRight` | 进入下一级(进入当前选中项的子菜单) |
| `ArrowLeft` | 返回上一级(退出当前层级) |
| `Enter` | 触发当前选中项的 `click` 事件 |
> 🔄 循环选择:到达末尾后回到开头,反之亦然。
---
## 事件处理
### `key_handler(event)`
主键盘事件处理器,仅当实例位于 `key_selectable_stack` 栈顶时生效。
- 使用 `event.key` 匹配方向键与回车
- 分发对应操作(上下选择、进出层级、确认)
### 生命周期事件
- `on_parent`:当控件被添加或移除父容器时派发,通知父子关系变更
---
## 方法说明
### 控件管理
| 方法 | 参数 | 描述 |
|------|------|------|
| `add_widget(w, index)` | `w`: widget, `index?`: number | 将控件插入指定位置或追加至末尾 |
| `remove_widget(w)` | `w`: widget | 移除指定控件,清理 DOM 和事件 |
| `clear_widgets()` | 无 | 清空所有子控件及 DOM 子节点 |
| `remove_widgets_at_begin(cnt)` | `cnt`: number | 移除前 `cnt` 个子控件 |
| `remove_widgets_at_end(cnt)` | `cnt`: number | 移除后 `cnt` 个子控件 |
> ✅ `insertBefore` / `appendChild` 被合理使用以维持 DOM 结构一致性。
---
### UI 构建方法
| 方法 | 功能 |
|------|------|
| `build_title()` / `build_title_widget()` | 创建并添加标题控件(`Title3` |
| `build_description()` / `build_description_widget()` | 添加描述文本(`Text` |
| `build_toolbar_widget(ext_tools)` | 构建浮动图标工具栏(`FloatTextIconBar` |
> 🔔 多数构建方法检查是否存在对应属性(如 `this.title`)后再创建。
---
### 选择控制方法
| 方法 | 功能 |
|------|------|
| `select_item(w)` | 选中指定控件,取消之前的选择 |
| `select_default_item()` | 默认选择第一个子控件 |
| `select_next_item()` | 向下循环选择 |
| `select_previous_item()` | 向上循环选择 |
| `find_first_keyselectable_child()` | 深度优先查找首个支持键盘选择的后代 |
---
### 层级跳转
| 方法 | 功能 |
|------|------|
| `down_level()` | 进入当前选中控件的子层级(需支持 keyselectable |
| `up_level()` | 退出当前层级,返回上级 |
> 🧭 类似菜单树导航模型,利用栈维护路径。
---
## 工厂注册
以下类型已向 `bricks.Factory` 注册,可用于声明式创建:
```js
bricks.Factory.register('HBox', bricks.HBox);
bricks.Factory.register('FHBox', bricks.FHBox);
bricks.Factory.register('VBox', bricks.VBox);
bricks.Factory.register('FVBox', bricks.FVBox);
bricks.Factory.register('Filler', bricks.Filler);
bricks.Factory.register('HFiller', bricks.Filler); // 水平填充
bricks.Factory.register('VFiller', bricks.Filler); // 垂直填充(同 Fillers
bricks.Factory.register('ResponsableBox', bricks.ResponsableBox);
```
> ✅ 支持模板引擎或 JSON 配置动态生成界面。
---
## 示例用法
### 创建带标题的垂直布局
```js
let vbox = new bricks.VBox({
title: "用户设置",
description: "请选择要修改的选项",
keyselectable: true
});
// 添加子控件
let btn1 = new bricks.Button({ otext: "个人资料" });
let btn2 = new bricks.Button({ otext: "安全设置" });
vbox.add_widget(btn1);
vbox.add_widget(btn2);
document.body.appendChild(vbox.dom_element);
```
### 启用手动键盘导航
```js
vbox.enable_key_select(); // 激活键盘控制
```
此时可用 ↑↓ 键切换按钮Enter 执行点击,← 返回上级(如果有)。
### 响应式容器示例
```js
let responsive = new bricks.ResponsableBox();
responsive.add_widget(new bricks.Label({ otext: "此布局会随尺寸变化!" }));
document.body.appendChild(responsive.dom_element);
```
窗口大小改变时,布局方向将自动调整。
---
## 注意事项与最佳实践
1. **避免重复启用 `keyselectable`**
- 确保 `enable_key_select()` 不被多次调用导致重复绑定事件
2. **手动管理栈结构风险**
- `key_selectable_stack` 是全局状态,跨组件交互需谨慎
3. **DOM 更新同步**
- 所有 `add_widget` / `remove_widget` 都应及时更新 DOM 和 `children` 数组
4. **性能优化建议**
- `find_first_keyselectable_child()` 使用递归,深层嵌套可能影响性能,可缓存结果
5. **i18n 支持**
- 所有文本控件启用 `i18n: true`,便于国际化
---
## 总结
`bricks.Layout` 提供了一个强大而灵活的基础布局系统,结合键盘导航与响应式能力,非常适合构建复杂的桌面风格 Web 应用界面(如仪表盘、菜单系统、表单布局等)。其模块化设计使得扩展新布局类型变得简单,同时工厂模式支持运行时动态渲染。
> ✅ 推荐在需要高可访问性Accessibility和键盘操作支持的应用中使用此组件体系。
---
📌 **版本信息**:本文档基于提供的 JavaScript 代码片段撰写
📅 **最后更新**2025年4月5日

View File

@ -1,203 +0,0 @@
# `bricks.ChartLine` 技术文档
> 基于 ECharts 扩展的折线图组件,用于快速渲染多系列折线图。
---
## 概述
`bricks.ChartLine``bricks.EchartsExt` 的子类,封装了基于 ECharts 的折线图配置逻辑。它支持从远程数据源或本地数据动态生成折线图,并自动构建图表所需的 `options` 配置对象。
该组件通过工厂模式注册为 `'ChartLine'` 类型,可通过 `bricks.Factory.create('ChartLine', config)` 实例化。
---
## 依赖
- `bricks.EchartsExt`:基础 ECharts 扩展类。
- `bricks.Factory`:用于组件注册与创建的工厂类。
---
## 属性(配置项)
| 属性名 | 类型 | 必填 | 描述 |
|----------------|----------|------|------|
| `data_url` | String | 否 | 数据请求的 URL 地址。若提供,则通过 AJAX 获取数据。 |
| `data_params` | Object | 否 | 请求数据时携带的参数GET 或 POST。 |
| `method` | String | 否 | 请求方法,默认为 `'GET'`。可选 `'POST'`。 |
| `data` | Array | 否 | 直接传入的本地数据数组,格式为对象数组。例如:`[{name: 'A', value1: 10, value2: 20}]`。 |
| `line_options` | Object | 否 | 自定义 ECharts 配置的扩展选项,会合并到最终的 `options` 中。 |
| `nameField` | String | 是 | 用作 X 轴分类字段的键名(如时间、类别等)。 |
| `valueFields` | Array<String> | 是 | 一个字符串数组表示需要绘制为折线的字段名称Y 轴数据)。每个字段将生成一条折线。 |
> ⚠️ 注意:`data``data_url` 二选一。若两者都存在,优先使用 `data_url` 异步加载数据。
---
## 方法
### `values_from_data(data, name)`
从数据数组中提取指定字段的所有值。
#### 参数
- `data` (Array<Object>):数据对象数组。
- `name` (String):要提取的字段名。
#### 返回值
- Array包含所有 `data[i][name]` 值的数组。
#### 示例
```js
const data = [{x: 'A', y: 1}, {x: 'B', y: 2}];
this.values_from_data(data, 'y'); // 返回 [1, 2]
```
---
### `lineinfo_from_data(data, name)`
生成单条折线的 ECharts 系列配置项。
#### 参数
- `data` (Array<Object>):原始数据。
- `name` (String):对应 `valueFields` 中的字段名,作为该折线的名称和数据来源。
#### 返回值
```js
{
name: String,
type: 'line',
data: Array // 提取自 data 中对应字段的值
}
```
#### 示例
```js
this.lineinfo_from_data(data, 'sales');
// 返回:
// {
// name: 'sales',
// type: 'line',
// data: [100, 150, 200]
// }
```
---
### `setup_options(data)`
根据输入数据构建完整的 ECharts 配置对象。
#### 参数
- `data` (Array<Object>):待处理的数据数组。
#### 返回值
返回标准的 ECharts `options` 对象,结构如下:
```js
{
tooltip: { trigger: 'axis' },
legend: { data: valueFields },
xAxis: {
type: 'category',
data: [/* 所有 nameField 对应的值 */]
},
yAxis: {
type: 'value'
},
series: [/* 多个 lineinfo_from_data 生成的对象 */]
}
```
#### 内部流程
1. 提取 `nameField` 字段作为 X 轴数据。
2. 遍历 `valueFields`,每项生成一条折线系列。
3. 构建图例legend`valueFields` 数组。
4. 设置提示框为轴触发(`axis`)。
5. 返回完整配置。
---
## 使用示例
### HTML 结构
```html
<div id="chart-line" style="width: 800px; height: 400px;"></div>
```
### JavaScript 初始化
#### 方式一:使用本地数据
```js
const chart = new bricks.ChartLine({
data: [
{ month: 'Jan', sales: 100, profit: 60 },
{ month: 'Feb', sales: 130, profit: 70 },
{ month: 'Mar', sales: 145, profit: 65 }
],
nameField: 'month',
valueFields: ['sales', 'profit']
});
chart.renderTo('#chart-line');
```
#### 方式二:从接口加载数据
```js
const chart = new bricks.ChartLine({
data_url: '/api/chart-data',
data_params: { year: 2023 },
method: 'GET',
nameField: 'date',
valueFields: ['pv', 'uv', 'orders']
});
chart.renderTo('#chart-line');
```
---
## 图表效果说明
- 支持多条折线同时展示。
- X 轴为类目轴category显示 `nameField` 的值。
- Y 轴为数值轴value
- 鼠标悬停时显示坐标轴对齐的提示框tooltip
- 图例自动根据 `valueFields` 生成。
---
## 注册信息
```js
bricks.Factory.register('ChartLine', bricks.ChartLine);
```
可通过工厂方式创建:
```js
const chart = bricks.Factory.create('ChartLine', config);
```
---
## 注意事项
1. 数据必须是对象数组格式。
2. `nameField``valueFields` 中的字段必须存在于数据对象中。
3. 若使用 `data_url`,后端需返回 JSON 格式数组。
4. 可通过 `line_options` 进行高级定制(如颜色、样式、动画等),会深度合并至最终 options。
---
## 版本信息
- 创建者:`bricks` 框架团队
- 类型名称:`ChartLine`
- 继承自:`bricks.EchartsExt`
- 注册标识符:`'ChartLine'`
---
✅ 推荐用于快速搭建基于 ECharts 的多系列折线图场景。

View File

@ -1,414 +0,0 @@
以下是针对您提供的 JavaScript 代码编写的 **Markdown 格式技术文档**,结构清晰、注释完整,适用于前端开发团队或项目维护者使用。
---
# 📘 `bricks.LlmIO` 模块技术文档
> 基于 `bricks.js` 框架实现的多模型大语言模型LLM交互系统
> 支持流式响应、文本转语音TTS、用户反馈评分等功能
---
## 🔧 模块结构概览
```js
bricks = window.bricks || {}
bricks.LlmMsgAudio // 音频播放与流式消息处理
bricks.ModelOutput // 模型输出 UI 组件
bricks.LlmModel // 单个 LLM 模型控制器
bricks.LlmIO // 主控容器:管理输入、多个模型和输出展示
```
所有组件均继承自 `bricks.js` 提供的基础 UI 类(如 `VBox`, `HBox`, `JsWidget` 等),并支持动态构建、事件绑定和异步数据流处理。
---
## 1. `bricks.LlmMsgAudio` —— 流式音频消息处理器
### 功能说明
用于处理来自后端的流式文本响应,并根据标点符号分段发送至音频播放器。自动检测语言以适配中英文标点。
### 继承关系
```js
class LlmMsgAudio extends bricks.UpStreaming
```
### 构造函数:`constructor(opts)`
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 传递给父类的配置项 |
#### 初始化字段:
- `this.olddata`: 上一次接收到的数据(用于增量比较)
- `this.data`: 当前累积的完整文本
- `this.cn_p`: 中文标点符号数组 `[“。”,”,”,”!”,”?”,”\n”]`
- `this.other_p`: 英文标点符号数组 `[“.”,”,”,”!”, “?”, “\n”]`
- `this.audio`: 实例化的音频播放器(通过 `AudioPlayer({})` 创建)
### 方法列表
#### ✅ `detectLanguage(text)``String`
尝试使用浏览器内置的 `Intl.LocaleDetector` 推测文本语言。
- 返回示例:`'zh'`, `'en'`
- 若失败则返回 `'未知'`
> ⚠️ 注意:`Intl.LocaleDetector` 并非所有浏览器都支持,需注意兼容性降级处理。
#### ✅ `send(data)``void`
接收增量数据并进行分句处理,将已完成句子通过 `super.send()` 发送。
##### 工作流程:
1. 计算新增部分:`newdata = data.slice(this.olddata.length)`
2. 更新历史数据缓存
3. 调用 `detectLanguage(this.data)` 判断语言
4. 使用对应标点分割成句子数组(过滤空字符串)
5. 将除最后一句外的所有完整句子发送出去
6. 保留最后未完成句在 `this.data` 中等待下一批数据
> 💡 示例:
> 输入 `"你好!今天天气"` → 输出 `"你好!"`,缓存 `"今天天气"`
#### ✅ `async go()``Promise<Response>`
调用父类 `go()` 获取最终响应,并设置音频源为该响应内容。
```js
await super.go();
this.audio.set_source_from_response(resp);
```
通常用于非流式场景下的 TTS 音频播放。
---
## 2. `bricks.ModelOutput` —— 模型输出 UI 控件
### 功能说明
显示单个模型的输出结果,包含图标、加载动画、内容区域及满意度评价功能。
### 继承关系
```js
class ModelOutput extends bricks.VBox
```
### 构造函数:`constructor(opts)`
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `modelname` | String | 是 | - | 显示的模型名称 |
| `icon` | URL/String | 否 | `llm.svg` | 自定义图标路径 |
| `response_mode` | String | 否 | - | 可选 `'stream'`, `'sync'`, `'async'` |
| `estimate_url` | URL | 否 | - | 用户反馈提交地址 |
#### 内部组件初始化:
- `img`: 模型图标 SVG
- `content`: 主体 HBox 容器
- `run`: 加载动画组件(`BaseRunning`
- `filler`: 实际内容显示区(`LlmOut` 组件)
- `estimate_w`: 满意度打分组件(点赞/踩)
### 核心方法
#### ✅ `build_estimate_widgets()``void`
创建“结果满意吗?”评分控件(仅当 `estimate_url` 存在时)。
包含:
- 文本提示:“结果满意吗?”
- 👍 点赞图标(绑定 `estimate_llm(1)`
- 👎 点踩图标(绑定 `estimate_llm(-1)`
初始状态为隐藏(`.hide()`),可在完成后显示。
#### ✅ `async estimate_llm(val, event)``Promise<void>`
用户点击点赞/点踩时触发,向服务端上报评分。
**请求参数:**
```json
{
"widgettype": "urlwidget",
"options": {
"url": this.estimate_url,
"params": {
"logid": this.logid,
"value": val // 1 或 -1
}
}
}
```
> 执行后禁用评分按钮防止重复提交。
#### ✅ `async update_data(data)``Promise<void>`
更新模型输出内容。
- 移除加载动画 `run`
- 调用 `filler.update(data)` 渲染内容
- 支持流式逐步更新
#### ✅ `finish()``void`
生命周期钩子,当前无实际逻辑,可扩展。
---
## 3. `bricks.LlmModel` —— 单个 LLM 模型控制器
### 功能说明
封装一个 LLM 模型的行为逻辑,包括请求发送、格式化、响应处理等。
### 继承关系
```js
class LlmModel extends bricks.JsWidget
```
### 构造函数:`constructor(llmio, opts)`
| 参数 | 类型 | 说明 |
|------|------|------|
| `llmio` | LlmIO 实例 | 上层控制容器引用 |
| `opts` | Object | 模型配置对象 |
#### 配置项 (`opts`) 支持字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `model` | String | 模型标识符(如 `gpt-3.5-turbo` |
| `modelname` | String | 显示名 |
| `url` | URL | 请求接口地址 |
| `params` | Object | 固定请求参数 |
| `input_from` | String | 允许的数据来源标签 |
| `textvoice` | Boolean | 是否启用 TTS 语音播报 |
| `tts_url` | URL | TTS 接口地址 |
| `response_mode` | String | `'stream' \| 'sync' \| 'async'` |
| `icon` | URL | 图标路径 |
### 核心方法
#### ✅ `render_title()``HBox`
生成顶部标题栏(含图标),点击可打开设置面板(预留接口)。
#### ✅ `show_setup_panel(event)``void`
待实现:点击标题弹出模型配置面板。
#### ✅ `inputdata2uploaddata(data)``FormData \| Object`
将输入数据转换为适合上传的格式,并注入模型相关信息。
- 若是 `FormData`,使用 `.append()` 添加字段
- 否则直接赋值对象属性
- 注入 `model``llmid`
> 特殊逻辑:若 `llmio.model_inputed` 为真,则不添加 `model` 字段(避免重复)
#### ✅ `async model_inputed(data)``Promise<void>`
主入口方法:接收用户输入并发起模型请求。
**行为分支:**
| 模式 | 行为 |
|------|------|
| `'stream'` / `'async'` | 使用 `HttpResponseStream` 处理流式响应 |
| `'sync'` | 使用 `HttpJson` 发起同步 POST 请求 |
流程:
1. 创建 `ModelOutput` 显示组件并加入页面
2. 准备请求数据
3. 根据模式发起请求
4. 流式情况下逐块调用 `chunk_response(mout, line)`
#### ✅ `is_accept_source(source)``Boolean`
判断是否接受来自指定源的数据。
#### ✅ `llm_msg_format()``Object`
返回默认的消息结构模板:
```js
{ role: 'assistant', content: "${content}" }
```
可用于格式化聊天记录。
#### ✅ `chunk_response(mout, l)``void`
处理每一个流式响应片段(每行 JSON 字符串)。
- 去除空白字符
- 尝试解析 JSON
- 异步模式下只处理 `status === 'SUCCEEDED'` 的消息
- 调用 `mout.update_data(d)` 更新 UI
#### ✅ `chunk_ended()``void`
流结束回调(目前仅打印日志)
---
## 4. `bricks.LlmIO` —— 主控容器LLM 输入输出管理器)
### 功能说明
集成式组件,提供以下能力:
- 多模型管理
- 输入表单弹窗
- 模型选择弹窗
- 自动渲染模型输出
- 支持 TTS 和用户反馈
### 继承关系
```js
class LlmIO extends bricks.VBox
```
### 构造函数:`constructor(opts)`
| 属性 | 类型 | 说明 |
|------|------|------|
| `user_icon` | URL | 用户头像图标 |
| `list_models_url` | URL | 获取可用模型列表的接口 |
| `input_fields` | Array | 表单字段定义(参考 `bricks.Form` |
| `models` | Array | 初始已添加的模型列表 |
| `tts_url` | URL | TTS 接口地址 |
| `estimate_url` | URL | 用户反馈提交地址 |
#### 初始化行为:
- 创建标题栏 `title_w`
- 创建输出区域 `o_w`(滚动容器)
- 创建底部操作按钮:
- `i_w`: 输入按钮(打开输入表单)
- `nm_w`: 添加模型按钮(打开模型搜索窗口)
- 绑定点击事件
- 若模型少于 2 个且存在 `tts_url`,启用 `textvoice` 模式
- 遍历 `models` 初始化每个 `LlmModel`
### 核心方法
#### ✅ `async show_input(params)``Promise<void>`
显示用户输入内容在对话区域上方。
- 构建 `UserInputView` 显示输入数据
- 添加用户头像图标
- 插入到 `o_w` 容器中
#### ✅ `show_added_model(m)``void`
注册一个新的模型实例并将其标题添加到顶部工具栏。
- 若启用了 `textvoice`,自动注入 `tts_url`
- 实例化 `LlmModel`
- 调用 `.render_title()` 并加入 `title_w`
#### ✅ `async open_search_models(event)``Promise<void>`
打开“选择模型”弹窗,从远程获取模型列表。
使用 `PopupWindow + Cols` 组件展示表格形式的模型列表。
**表格列定义:**
- 图标 + 名称HBox
- 描述、启用时间VBox 内嵌 Text
点击某条记录会触发:
- `add_new_model(event)`
- 并关闭弹窗
#### ✅ `async add_new_model(event)``Promise<void>`
将选中的模型加入 `this.models` 数组,并调用 `show_added_model` 显示。
#### ✅ `async open_input_widget(event)``Promise<void>`
打开输入表单弹窗。
使用 `Form` 组件加载 `input_fields` 定义的字段。
提交后触发:
- `handle_input(event)`
- 并自动关闭弹窗
#### ✅ `async handle_input(event)``Promise<void>`
处理用户提交的输入数据。
1. 调用 `show_input(params)` 显示输入内容
2. 遍历所有 `llmmodels`,调度执行各自的 `model_inputed(params)`
- 使用 `schedule_once(fn, 0.01)` 实现微任务延迟执行
---
## 📦 注册组件
```js
bricks.Factory.register('LlmIO', bricks.LlmIO);
```
允许通过工厂方式动态创建此组件。
---
## 🎯 使用示例
```js
var llmio = new bricks.LlmIO({
user_icon: '/static/icons/user.png',
tts_url: '/api/tts',
estimate_url: '/api/feedback',
input_fields: [
{ name: 'prompt', label: '输入提示', type: 'textarea' }
],
models: [
{
model: 'qwen-plus',
modelname: '通义千问',
url: '/api/llm/stream',
response_mode: 'stream',
icon: '/icons/qwen.svg'
}
]
});
```
---
## 🛠️ 注意事项 & 待优化点
| 问题 | 建议修复 |
|------|----------|
| `detectLaguage` 拼写错误 | 应为 `detectLanguage` |
| `this.oter_p` 拼写错误 | 应为 `this.other_p` |
| `lang='zh'` 错误地使用了赋值而非比较 | 应改为 `lang === 'zh'` |
| `objcopy` 函数未定义 | 应替换为 `JSON.parse(JSON.stringify(data))` 或引入深拷贝工具 |
| `Intl.LocaleDetector` 兼容性差 | 建议降级为基于关键词的语言识别算法 |
| `schedule_once(..., 0.01)` 不标准 | 建议改用 `setTimeout(fn, 0)``queueMicrotask` |
---
## 📚 附录关键类图UML 简化版)
```
+------------------+
| LlmIO |
+------------------+
|
v
+------------------+
| LlmModel[x] |
+------------------+
|
v
+------------------+
| ModelOutput |
+------------------+---> LlmMsgAudio (用于音频)
|
v
+------------------+
| UserInput |
+------------------+
```
---
## 📝 总结
本模块实现了完整的 LLM 交互链路:
1. 用户输入 → 表单收集
2. 多模型并发调用 → 支持流式/同步
3. 分句播放音频 → 提升听觉体验
4. 用户反馈机制 → 支持效果评估闭环
适合集成于智能客服、AI 助手、教育平台等需要多模型对比或语音播报的应用场景。
---
**文档版本v1.0**
📅 最后更新2025年4月5日
👨‍💻 编写Bricks Framework Team

View File

@ -1,366 +0,0 @@
# `bricks.js` 技术文档LlmDialog 模块
> **版本**1.0
> **模块名称**`LlmDialog`, `LlmMsgBox`, `UserMsgBox`, 工具函数 `escapeSpecialChars`
> **依赖框架**`bricks.js` 前端组件库(基于类的 UI 架构)
---
## 📚 概述
该模块提供了一套用于构建与 LLM大语言模型交互对话界面的组件系统支持多模型并行响应、流式/同步响应模式、Markdown 渲染以及消息内容转义。主要包含以下核心类:
- `bricks.escapeSpecialChars`:字符串特殊字符转义工具。
- `bricks.UserMsgBox`:用户消息展示组件。
- `bricks.LlmMsgBox`LLM 回应消息展示及请求处理组件。
- `bricks.LlmDialog`:主对话容器,管理整个对话流程。
此外,通过 `bricks.Factory.register` 注册了 `LlmDialog` 以便动态创建。
---
## 🔧 工具函数
### `bricks.escapeSpecialChars(s)`
对输入字符串中的特殊字符进行反斜杠转义,常用于防止 JSON 注入或确保文本安全输出。
#### 参数
| 参数 | 类型 | 描述 |
|------|--------|--------------|
| `s` | string | 待转义字符串 |
#### 返回值
- **类型**`string`
- 转义后的字符串,适用于嵌入 JSON 或 HTML 环境。
#### 支持转义的字符
| 字符 | 原始表示 | 转义后表示 | 说明 |
|------|----------|----------------|--------------------|
| `\` | `\` | `\\` | 反斜杠 |
| `"` | `"` | `\"` | 双引号 |
| `\n` | 换行 | `\n` | 换行符 |
| `\r` | 回车 | `\r` | 回车符 |
| `\t` | 制表符 | `\t` | Tab |
| `\f` | 换页 | `\f` | Form feed |
| `\v` | 垂直制表 | `\v` | Vertical tab |
| `\0` | 空字符 | `\0` | Null byte |
> ⚠️ 注意:单引号 `'` 的转义代码被注释,当前未启用。
#### 示例
```js
bricks.escapeSpecialChars('Hello "world"\n');
// 输出: 'Hello \\"world\\"\\n'
```
---
## 💬 用户消息组件:`UserMsgBox`
继承自 `bricks.HBox`,用于显示用户发送的消息。
### 继承关系
```js
class UserMsgBox extends bricks.HBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------|--------|------|----------------------------|------------------------------|
| `icon` | string | 否 | `imgs/chat-user.svg` | 用户头像 SVG 图标 URL |
| `prompt` | string | 是 | - | 用户输入的消息内容 |
| `msg_css` | string | 否 | `'user_msg'` | 应用到消息体的 CSS 类名 |
#### 内部结构
1. 创建一个 SVG 头像图标(使用 `bricks.Svg`)。
2. 使用 `MdWidget` 显示 Markdown 格式消息。
3. 添加左侧空白图标占位符(`BlankIcon`),保持布局对齐。
4. 消息排列顺序:`[BlankIcon] ← [消息内容] ← [用户头像]`
#### 示例实例化
```js
new bricks.UserMsgBox({
prompt: "你好,请介绍一下你自己。",
icon: "/icons/user.svg",
msg_css: "custom-user-style"
});
```
---
## 🤖 LLM 消息组件:`LlmMsgBox`
继承自 `bricks.HBox`,用于展示 LLM 的回复,并支持流式和同步两种响应方式。
### 继承关系
```js
class LlmMsgBox extends bricks.HBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------------|----------|------|-------------------------|----------------------------------------------------------------------|
| `model` | string | 是 | - | 使用的模型名称(如 `gpt-3.5-turbo` |
| `mapi` | string | 是 | - | API 类型标识(可用于后端路由判断) |
| `url` | string | 是 | - | 请求后端接口地址 |
| `icon` | string | 否 | `imgs/user.png` | LLM 头像图标 URL |
| `msg_css` | string | 否 | `'llm_msg'` | 消息区域应用的 CSS 类 |
| `response_mode` | string | 否 | `'stream'` | 响应模式:`'stream'`, `'sync'`, `'async'` |
| `user_msg` | object | 否 | `{role:'user', content:"${prompt}"}` | 用户消息模板,支持 `${prompt}` 插值 |
| `llm_msg` | object | 否 | `{role:'assistant', content:"${content}"}` | LLM 消息模板,支持 `${content}` 插值 |
#### 内部初始化逻辑
- 创建头像图标SVG
- 初始化 `MdWidget` 显示消息内容
- 加载 `BaseRunning` 动画组件(在响应中显示加载状态)
- 初始化消息历史数组 `this.messages = []`
#### 消息排列顺序
`[LLM头像] → [运行动画] → [消息内容] → [空白占位符]`
---
### 方法列表
#### `responsed()`
标记响应已开始,停止加载动画,移除运行指示器。
```js
this.run.stop_timepass();
this.remove_widget(this.run);
```
#### `user_msg_format()`
生成用户消息对象格式。
- 若设置了 `this.user_msg`,则返回其值;
- 否则返回默认模板:`{role: 'user', content: "${prompt}"}`
> 实际内容将在调用时通过 `bricks.apply_data()` 替换 `${prompt}`
#### `llm_msg_format()`
生成 LLM 消息对象格式。
- 若设置了 `this.llm_msg`,返回其值;
- 否则返回:`{role: 'assistant', content: "${content}"}`
#### `chunk_response(l)`
处理流式响应中的每一个数据块chunk
##### 参数
- `l`: JSON 字符串片段(通常来自 ReadableStream
##### 行为
1. 解析 JSON 数据。
2. 若无 `content` 字段或为空,忽略。
3. 累加内容至当前消息。
4. 更新 `MdWidget` 内容。
5. 触发 `updated` 事件。
#### `chunk_ended()`
流式响应结束后,将最终内容保存为结构化消息并推入 `messages` 数组。
- 调用 `escapeSpecialChars` 对内容转义
- 使用 `apply_data` 填充模板
- 推入 `this.messages`
#### `async set_prompt(prompt)`
发起对 LLM 的请求。
##### 参数
- `prompt`: 用户输入文本(自动转义)
##### 流程
1. 将用户消息按模板格式化并加入 `messages`
2. 准备请求参数:
```js
{
messages: this.messages,
mapi: this.mapi,
model: this.model
}
```
3. 根据 `response_mode` 执行不同请求策略:
| 模式 | 行为说明 |
|-----------|---------|
| `stream` | 使用 `HttpResponseStream` 发起 POST 请求,逐块接收并渲染 |
| `sync` | 使用 `HttpJson` 同步获取完整响应,一次性渲染结果 |
| 其他 | 不做任何操作(预留扩展) |
##### 示例调用
```js
await llmMsgBox.set_prompt("请写一首关于春天的诗");
```
---
## 🧩 主对话框组件:`LlmDialog`
继承自 `bricks.VBox`,是完整的聊天对话界面容器,支持多模型同时响应。
### 继承关系
```js
class LlmDialog extends bricks.VBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------------|----------|------|----------------|----------------------------------------------------------------------|
| `models` | array | 是 | - | 模型配置数组,每个元素定义一个 LLM 模型 |
| `response_mode` | string | 否 | `'stream'` | 全局响应模式:`'stream'`, `'sync'`, `'async'` |
| `user_msg_css` | string | 否 | `'user_msg'` | 用户消息使用的 CSS 类 |
| `user_icon` | string | 否 | - | 用户头像图标路径 |
| `title_ccs` | string | 否 | `'llm_title'` | 标题栏 CSS 类(拼写疑似错误,应为 `title_css` |
| `height` | string | 否 | `'100%'` | 容器高度 |
#### `models` 数组项结构
```js
{
model: "gpt-3.5-turbo",
mapi: "openai",
url: "/api/openai/chat",
icon: "/icons/gpt.svg",
css: "gpt-response",
user_msg: { role: "user", content: "${prompt}" },
llm_msg: { role: "assistant", content: "${content}" }
}
```
---
### 方法列表
#### `show_models_info()`
遍历所有模型,调用 `show_model_info(model)` 在标题栏显示模型信息。
#### `show_model_info(model)`
在顶部标题区添加一个 HBox包含模型图标和名称。
- 使用 `Svg` 显示图标
- 使用 `Text` 显示模型名
- 存储引用到 `this.model_info_ws[model.model]`
#### `add_model(model)`
动态添加新模型配置,并立即显示在标题栏。
#### `delete_model(model)`
根据 `model.model` 名称删除模型及其视图。
#### `async set_prompt(prompt)`
主入口方法:发送用户消息并触发所有模型响应。
##### 步骤
1. 转义用户输入。
2. 创建并添加 `UserMsgBox` 到滚动面板。
3. 调用 `llm_request(prompt)` 并等待完成。
4. 自动滚动到底部。
#### `async llm_request(prompt)`
为每个注册的模型创建一个 `LlmMsgBox`,并异步启动请求。
- 使用 `schedule_once(fn, 0.1)` 延迟执行以避免阻塞 UI
- 所有模型并行响应
---
### 事件机制
#### `llm_answer` 事件
当某个模型完成响应后,可通过监听此事件获取回答内容。
> ❗ 当前代码中未显式触发 `llm_answer` 事件,需补充如下代码才能生效:
```js
// 在 chunk_ended 或 sync 响应结束处添加:
this.fire('llm_answer', { content: txt });
```
建议增强事件通知能力。
---
## 🏗️ 工厂注册
```js
bricks.Factory.register('LlmDialog', bricks.LlmDialog);
```
允许通过字符串标识动态创建组件:
```js
var dialog = bricks.Factory.create('LlmDialog', options);
```
---
## ✅ 使用示例
```js
var dialog = new bricks.LlmDialog({
height: '600px',
user_icon: '/icons/me.svg',
title_ccs: 'chat-title',
response_mode: 'stream',
models: [
{
model: 'gpt-3.5-turbo',
mapi: 'openai',
url: '/api/chat',
icon: '/icons/gpt.svg',
css: 'gpt-msg',
user_msg: { role: 'user', content: '${prompt}' },
llm_msg: { role: 'assistant', content: '${content}' }
},
{
model: 'claude-2',
mapi: 'anthropic',
url: '/api/anthropic',
icon: '/icons/claude.svg',
css: 'claude-msg'
}
]
});
document.body.appendChild(dialog.dom_element);
// 发送消息
dialog.set_prompt("解释什么是机器学习");
```
---
## 📝 注意事项与改进建议
| 项目 | 说明 |
|------|------|
| 🔐 安全性 | `escapeSpecialChars` 有助于防止注入,但建议结合 DOMPurify 或其他 sanitizer 进一步防护 XSS |
| 🧩 单引号转义 | 被注释掉,若需兼容 SQL 或属性字符串场景,建议启用 |
| 🎯 事件缺失 | `llm_answer` 事件未实际触发,建议在 `chunk_ended()` 中补全 |
| 🖼️ 图标资源 | 依赖 `bricks_resource()` 函数解析静态资源路径,需确保其存在 |
| ⏳ 异步控制 | `schedule_once(..., 0.1)` 是一种 Hack可考虑改为 `queueMicrotask``Promise.resolve().then()` |
| 📏 布局兼容 | 使用 FlexboxHBox/VBox确保父级容器设置正确尺寸 |
---
## 📚 总结
本模块提供了完整的前端 LLM 聊天对话解决方案,具备以下特性:
✅ 多模型支持
✅ 流式 & 同步响应
✅ Markdown 渲染
✅ 自动滚动与加载动画
✅ 可扩展模板机制
适合集成进低代码平台、AI 助手界面、多模型对比测试工具等场景。
---
> 文档版本1.0
> 最后更新2025年4月5日

View File

@ -1,297 +0,0 @@
# Bricks 框架技术文档
## 概述
本文档描述了 `bricks` 框架中两个核心视图类的实现与使用:`UserInputView``LlmOut`。这两个类用于处理用户输入和大模型输出内容的可视化展示,支持 Markdown 文本、图像、音频、视频等多种媒体类型。
---
## 1. `bricks.UserInputView`
### 类定义
```javascript
bricks.UserInputView = class extends bricks.VBox
```
### 功能说明
将用户的输入字段(如文本、图片、音视频等)转换为结构化的 Markdown 内容进行展示,并独立嵌入音视频播放器组件。
- 所有非音视频字段以代码块形式显示。
- 图像字段以 Markdown 图像语法渲染。
- 视频和音频字段通过专用播放器组件独立展示。
---
### 构造函数
```javascript
constructor(opts)
```
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项,继承自 `bricks.VBox` |
#### 初始化行为
- 调用父类构造函数 `super(opts)`
- 初始化内部变量:
- `this.v_w`: 视频播放器实例(初始为 `null`
- `this.a_w`: 音频播放器实例(初始为 `null`
- 调用 `show_input(this.data)` 显示数据
---
### 方法:`show_input(data)`
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `data` | Object | 包含用户输入数据的对象 |
#### 处理逻辑
遍历 `this.input_fields` 中定义的字段配置,根据字段名前缀判断类型并生成相应内容:
| 字段前缀 | 处理方式 |
|---------|----------|
| `video*` | 创建 `VideoPlayer` 组件,自动播放,宽度 100% |
| `audio*` | 创建 `AudioPlayer` 组件,自动播放,宽度 100% |
| `image*` | 在 Markdown 中添加图片语法:`![label](url)` |
| 其他 | 使用代码块包裹内容:`\`\`\`\nvalue\n\`\`\`` |
> **注意**:字段标签优先使用 `f.label`,若无则使用 `f.name`
#### 流程步骤
1. 初始化空字符串 `mdtext`
2. 遍历所有输入字段,拼接 Markdown 内容或创建媒体组件
3. 清除当前所有子组件(`clear_widgets()`
4. 创建新的 `MdWidget` 显示 Markdown 内容
5. 添加音视频组件(如有)
#### 示例输出 Markdown
```markdown
* 用户提问
```
这是用户的输入文本
```
* 示例图片
![示例图片](https://example.com/image.png)
```
---
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `v_w` | VideoPlayer 或 null | 当前绑定的视频播放器 |
| `a_w` | AudioPlayer 或 null | 当前绑定的音频播放器 |
| `input_fields` | Array | 字段定义数组,需在外部设置 |
| `data` | Object | 输入数据对象 |
---
## 2. `bricks.LlmOut`
### 类定义
```javascript
bricks.LlmOut = class extends bricks.VBox
```
### 功能说明
用于动态渲染大语言模型LLM返回的结果数据。支持以下内容类型
- 推理过程文本thinking / reasoning
- 最终回答内容
- 错误信息
- 音频响应URL 或 Base64
- 视频响应URL 或 Base64
- 单张或多张图像
该组件具备增量更新能力,可通过多次调用 `update()` 累积内容。
---
### 构造函数
```javascript
constructor(opts)
```
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项,继承自 `VBox` |
#### 初始化行为
- 调用父类构造函数
- 初始化各类媒体组件引用为 `null`
- 初始化数据缓存属性为空值或空数组
#### 初始状态
```js
this.rc_w = null; // reasoning widget
this.c_w = null; // content widget
this.v_w = null; // video player
this.i_w = null; // image widgets (not used directly)
this.a_w = null; // audio player
this.images = []; // 存储图片 URL 列表
this.reasoning_content = ''; // 缓存推理内容
this.content = ''; // 缓存应答内容
this.error = ''; // 缓存错误信息
```
---
### 方法:`update(data)`
#### 参数
| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `data` | Object | 是 | LLM 返回的 JSON 数据 |
#### 支持字段
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `reasoning_content` | String | 推理过程文本(可选) |
| `content` | String | 最终回复文本(可选) |
| `error` | String | 错误信息(可选) |
| `audio` | String | 音频资源路径或 Base64 编码(可选) |
| `video` | String | 视频资源路径或 Base64 编码(可选) |
| `image` | String 或 Array<String> | 图片资源地址(单个或多个) |
---
#### 处理逻辑详解
##### 🔊 音频处理
- 若 `audio` 不以 `http` 开头且不包含 `data:audio/` 前缀,则自动封装为 `data:audio/wav;base64,...`
- 第一次出现时创建 `AudioPlayer`
- 后续调用会追加新音频(`add_url`
##### 🎥 视频处理
- 第一次出现创建 `VideoPlayer`
- 后续更新追加新视频源(`add_url`
##### ❌ 错误处理
- 将 `data.error` 追加到本地缓存 `this.error`
- 展示时使用 `resp-error` CSS 类样式化
##### 💬 推理内容
- 追加至 `this.reasoning_content`
- 使用 `thinking-content` 样式及浅红色背景 (`#f0d0d0`) 区分显示
##### ✅ 正常响应内容
- 追加至 `this.content`
- 使用 `resp-content` 样式正常展示
##### 🖼️ 图像处理
- 支持单个 URL 或数组
- 所有图像 URL 被合并进 `this.images` 数组
- 每张图创建一个 `Image` 组件并添加到容器
---
#### 渲染顺序
组件按如下顺序依次添加到界面:
1. 错误信息(若有)
2. 推理内容(若有)
3. 正常响应内容(若有)
4. 视频播放器(若有)
5. 音频播放器(若有)
6. 所有图片(逐一添加)
> **注意**:每次 `update()` 都会先清除已有组件(`clear_widgets()`),再重新构建整个 UI。
---
### 注册信息
```js
bricks.Factory.register('LlmOut', bricks.LlmOut);
```
允许通过工厂模式以字符串 `'LlmOut'` 实例化此类。
---
## 使用示例
### 示例 1初始化 UserInputView
```js
const userInput = new bricks.UserInputView({
data: {
question: "你好吗?",
image_01: "https://example.com/photo.jpg"
},
input_fields: [
{ name: "question", label: "问题" },
{ name: "image_01", label: "上传图片" }
]
});
```
### 示例 2更新 LLM 输出
```js
const llmView = new bricks.LlmOut();
llmView.update({
reasoning_content: "正在分析用户的问题...",
audio: "base64encodedstring==",
image: ["img1.jpg", "img2.jpg"]
});
llmView.update({
content: "我已经完成分析,这是结果。",
video: "https://example.com/demo.mp4"
});
```
---
## 依赖说明
| 组件 | 用途 |
|------|------|
| `bricks.VBox` | 布局容器基类,垂直排列子组件 |
| `bricks.MdWidget` | 渲染 Markdown 内容 |
| `bricks.VideoPlayer` | 视频播放组件 |
| `bricks.AudioPlayer` | 音频播放组件 |
| `bricks.Image` | 图像显示组件 |
| `bricks.escapeSpecialChars()` | 工具函数,转义特殊字符防止 XSS |
---
## 注意事项
1. **Base64 音频格式假设**:默认非 HTTP 音频数据为 WAV 格式的 Base64 编码。
2. **图像数组合并问题**:当前 `concat()` 未正确赋值,应改为:
```js
this.images = this.images.concat(data.image);
```
3. **增量更新限制**:虽然内容是累积的,但每次都会重绘全部组件,可能影响性能。
4. **样式依赖**:需要预定义 CSS 类:
- `resp-error`
- `thinking-content`
- `resp-content`
---
## 待优化建议
| 项目 | 建议 |
|------|------|
| 图像去重 | 可增加 URL 去重机制 |
| 媒体并发 | 多个音视频同时播放可能干扰用户体验 |
| 安全性 | `escapeSpecialChars` 应确保防御 XSS 注入 |
| 性能 | 大量文本更新时避免频繁 DOM 重绘 |
---
## 版本信息
- 框架Bricks UI Framework
- 模块:`UserInputView`, `LlmOut`
- 作者:系统自动生成文档
- 时间2025年4月5日
---
✅ 文档结束

View File

@ -1,250 +0,0 @@
# Bricks Markdown 组件技术文档
> **项目依赖说明**:本组件基于 [marked.js](https://github.com/markedjs/marked) 实现 Markdown 渲染功能。在使用 `bricks.js` 前,需确保已引入 `marked.min.js`
```html
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
```
---
## 模块概述
`bricks.MdWidget``bricks.MarkdownViewer` 是 Bricks 框架中用于渲染和交互式浏览 Markdown 内容的两个核心组件:
- `MdWidget`:基础 Markdown 显示控件,支持本地文本或远程 URL 加载。
- `MarkdownViewer`:增强型 Markdown 浏览器,内置导航栈(前进/后退)功能,适合构建文档阅读器类应用。
---
## 1. `bricks.MdWidget`
### 类定义
```js
class MdWidget extends bricks.JsWidget
```
### 功能描述
一个轻量级的 Markdown 渲染控件,可从字符串 (`mdtext`) 或远程 URL (`md_url`) 加载内容,并使用 `marked.js` 进行解析渲染。支持动态更新内容和链接拦截处理。
### 构造函数参数:`options`
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `mdtext` | String | 否 | 要渲染的 Markdown 文本内容 |
| `md_url` | String | 否 | 远程 Markdown 文件 URL |
| `method` | String | 否(默认 `"GET"` | 请求方式(目前仅支持 GET |
| `params` | Object | 否 | 请求参数(暂未实际用于 GET 请求) |
> ⚠️ 注意:若同时提供 `mdtext``md_url`,优先使用 `mdtext` 并立即渲染。
### 方法列表
#### `constructor(options)`
初始化组件:
- 若提供了 `mdtext`,则直接调用 `_build1()` 渲染。
- 否则延迟加载 `md_url` 内容,并绑定滚动监听。
#### `set_content(content)`
动态设置 Markdown 内容并重新渲染。
- **参数**
- `content`: (String) 新的 Markdown 字符串
#### `show_scroll(event)`
滚动事件回调,输出当前 `window.scrollY` 到调试日志(通过 `bricks.debug`)。
#### `async build()`
异步加载并渲染来自 `opts.md_url` 的 Markdown 内容。若未指定 URL则不执行任何操作。
#### `async _build(md_url)`
私有方法:通过 `bricks.tget(md_url)` 获取远程 Markdown 内容并渲染。
- 触发事件:`loaded`,携带 `{ url: md_url }`
- 自动重写页面内所有 `<a>` 标签为内部跳转行为
#### `_build1()`
将当前 `this.md_content` 使用 `marked.parse()` 解析为 HTML并插入 DOM。
同时劫持所有链接点击事件,实现内部导航。
> 所有 `<a href="...">` 被改为 `href="#"`,并绑定 `onclick` 调用 `_build(原URL)`
#### `getname()`
返回控件名称:
- 若设置了 `this.name`,返回该值;
- 否则返回 `'mdtext'`
#### `getValue()`
获取当前控件的值,格式为对象:
```js
{ [name]: md_content }
```
常用于表单数据收集。
#### `setValue(v)`
设置 Markdown 内容(仅赋值,不触发渲染)。通常配合其他逻辑调用。
---
## 2. `bricks.MarkdownViewer`
### 类定义
```js
class MarkdownViewer extends bricks.VBox
```
### 功能描述
一个容器式 Markdown 浏览器,继承自 `VBox`,支持以下特性:
- 可选的“返回”按钮(导航栈)
- 支持点击 Markdown 中的链接进行页面跳转
- 记录访问历史back_stack支持回退至上一页
### 构造函数参数:`options`
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `navigator` | Boolean | 否(默认 `true` | 是否显示返回按钮 |
| `recommentable` | Boolean | 否 | (预留字段,当前未使用) |
| `md_url` | String | 否 | 初始加载的远程 Markdown URL |
| `mdtext` | String | 否 | 初始本地 Markdown 文本 |
| `method` | String | 否(默认 `"GET"` | HTTP 方法 |
| `params` | Object | 否 | 请求参数对象 |
> ✅ 提示:使用 `absurl()` 处理相对路径,确保 URL 正确解析。
### 方法列表
#### `constructor(options)`
初始化组件:
- 创建内部 `MdWidget` 实例负责渲染
- 注册 `loaded` 事件以维护导航栈
- 设置样式:自动滚动、占满父容器高度
#### `show_scroll(event)`
`MdWidget`,打印当前滚动位置。
#### `async createBackButton()`
动态创建顶部返回按钮HBox + Text 组合):
- 显示文本 `"<<<<<<<"`
- 点击触发 `go_back()`
- 使用 `widgetBuild` 异步生成 UI 元素(调试信息较多)
> 🛠️ 当前 UI 较原始,未来可替换为图标或更美观样式。
#### `add_back_stack(event)`
当新页面加载完成时,将当前 URL 推入 `back_stack` 数组。
- **事件参数**`event.params.url` 表示刚加载的 URL
#### `async go_back(event)`
实现“返回上一页”逻辑:
1. 弹出当前页 URL
2. 再弹出上一页 URL
3. 使用 `mdtext._build(url)` 加载上一页内容
> 🔒 若栈长度小于 2则禁止返回。
#### `async build()` / `async _build(md_url)`
> ❗注意:此处存在代码冗余 —— `_build` 方法在 `MarkdownViewer` 中重复定义但未被调用(应调用的是 `mdtext._build`)。
建议移除该方法或修正逻辑,避免混淆。
---
## 事件系统
### 自定义事件
| 事件名 | 触发时机 | 携带参数 |
|--------|----------|---------|
| `loaded` | Markdown 内容成功加载并渲染后 | `{ url: string }` |
| `scroll` | 容器发生滚动时(绑定到 DOM | 原生 Event 对象 |
可通过 `.bind('event', handler)` 监听。
---
## 工厂注册
```js
bricks.Factory.register('MarkdownViewer', bricks.MarkdownViewer);
bricks.Factory.register('MdWidget', bricks.MdWidget);
```
允许通过 JSON 描述符动态创建组件:
### 示例 JSON 配置
```json
{
"widgettype": "MarkdownViewer",
"options": {
"md_url": "/docs/intro.md",
"navigator": true
}
}
```
---
## 使用示例
### 示例 1直接渲染本地 Markdown
```js
const mdWidget = new bricks.MdWidget({
mdtext: "# Hello\nWelcome to Bricks Markdown Viewer!"
});
document.body.appendChild(mdWidget.dom_element);
```
### 示例 2加载远程 Markdown 并启用导航
```js
const viewer = new bricks.MarkdownViewer({
md_url: 'https://example.com/README.md',
navigator: true
});
document.body.appendChild(viewer.dom_element);
```
### 示例 3动态更新内容
```js
mdWidget.set_content("## New Content\nUpdated at " + new Date().toLocaleString());
```
---
## 注意事项 & 建议
1. **依赖必须提前加载**
```html
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
```
2. **安全性提示**
- `innerHTML = marked.parse(...)` 存在 XSS 风险
- 如需展示用户输入,请先对 HTML 输出做净化处理(如使用 DOMPurify
3. **性能优化建议**
- 避免频繁调用 `set_content``_build`
- 对大型文档考虑分页或懒加载
4. **代码改进建议**
- `MarkdownViewer._build` 方法冗余且可能错误,建议删除或重构
- `createBackButton` 应支持国际化或自定义文本
- 链接拦截逻辑可抽象成独立方法便于复用
---
## 版本信息
- **作者**Bricks Framework 团队
- **依赖库**[marked@latest](https://marked.js.org/)
- **兼容性**现代浏览器ES6+ 支持)
---
📌 **文档版本**v1.0
📅 **最后更新**2025年4月5日

View File

@ -1,367 +0,0 @@
# `bricks.Menu` 技术文档
> 本文档为 `bricks.Menu` 类的详细说明,基于其继承自 `bricks.VBox` 的结构和功能设计。
---
## 概述
`bricks.Menu` 是一个用于构建可交互菜单界面组件的类,继承自 `bricks.VBox`。它支持嵌套子菜单、动态加载远程子菜单内容,并能响应菜单项点击事件以打开新窗口或更新目标组件内容。
该组件适用于构建侧边栏菜单、导航菜单或任何树形结构的交互式 UI。
---
## 继承关系
- **父类**: `bricks.VBox`
- **类型**: 自定义 UI 组件类Class-based
---
## 构造函数
```js
constructor(options)
```
### 参数
| 参数名 | 类型 | 说明 |
|-------|------|------|
| `options` | Object | 配置选项对象,继承自 `VBox` 并扩展以下属性 |
#### `options` 支持字段:
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `items` | Array | ✅ | - | 菜单项数组,每个元素是一个菜单项配置对象(见下文) |
| `bgcolor` | String | ❌ | `"white"` | 背景颜色 |
| `target` | String | ❌ | - | 点击菜单项后内容加载的目标组件 ID 或特殊值 `'PopupWindow'` / `'Popup'` |
| `popup_options` | Object | ❌ | `{}` | 当目标为 PopupWindow 或 Popup 时使用的弹窗配置 |
> ⚠️ 注意:`target``popup_options` 可在单个菜单项中覆盖。
### 示例
```js
new bricks.Menu({
items: [
{ label: "首页", url: "/home" },
{ label: "用户管理", submenu: "/api/users" }
],
target: "main_content",
bgcolor: "#f5f5f5"
});
```
---
## 核心方法
### `create_submenu_container()`
创建一个用于容纳子菜单的容器(`VBox`),默认隐藏并带有缩进样式。
#### 返回值
- `{bricks.VBox}` —— 已配置好样式的子菜单容器
#### 样式设置
- `marginLeft: "15px"`
- `display: "none"`
---
### `async menu_clicked(event)`
处理菜单项被点击后的逻辑,根据配置打开页面、弹窗或注入到指定组件。
#### 参数
- `event`: DOM 事件对象,包含 `params` 字段,即菜单项数据。
#### 行为逻辑
1. 解析目标类型:
- 若 `target === 'PopupWindow'`:创建 `PopupWindow` 实例
- 若 `target === 'Popup'`:创建 `Popup` 实例
- 否则:查找对应 ID 的组件 (`bricks.getWidgetById`)
2. 使用 `widgetBuild` 动态生成 URL Widget 并插入目标组件
3. 触发全局 `command` 事件,携带原始菜单项参数
#### 触发事件
- `command`:携带菜单项信息,可用于外部监听执行命令
#### 日志输出
- 失败时打印错误日志:
- 目标组件未找到
- widget 构建失败
---
### `create_children(w, items)`
递归创建菜单项及其子菜单。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `w` | `bricks.Container` | 容器组件,用于添加生成的菜单项 |
| `items` | Array | 菜单项列表 |
#### 子菜单类型判断
| 条件 | 类型 | 说明 |
|------|------|------|
| `item.items` 存在 | 静态子菜单 | 直接递归创建 |
| `item.submenu` 存在 | 动态子菜单 | 点击时通过 URL 加载 |
| 其他 | 叶节点菜单项 | 绑定点击事件触发 `item_click` |
#### 内部行为
- 创建 `HBox` 表示菜单项
- 添加图标与文本
- 绑定点击/展开事件
- 支持国际化i18n、换行、左对齐等文本特性
---
### `async get_submenu_items(url)`
从远程 URL 获取子菜单数据JSON 格式)。
#### 参数
- `url` (String): 请求地址
#### 返回值
- `Promise<Array>`: 菜单项数组(`data.options.items`
#### 示例响应格式
```json
{
"options": {
"items": [
{ "label": "子项1", "url": "/sub1" },
{ "label": "子项2", "url": "/sub2" }
]
}
}
```
---
### `async load_submenu(container, event)`
懒加载动态子菜单内容。
#### 参数
- `container`: 子菜单容器(`VBox`
- `event`: 点击事件
#### 流程
1. 阻止事件冒泡
2. 若未加载过,则调用 `get_submenu_items()` 获取数据
3. 调用 `create_children()` 填充内容
4. 切换显示/隐藏状态
> ✅ 实现“点击展开 → 首次加载 → 缓存”机制
---
### `items_toggle_hide(w, event)`
切换静态子菜单的可见性。
#### 参数
- `w`: 子菜单容器
- `event`: 事件对象
#### 行为
- 执行 `toggle_hide()` 显示或隐藏
- 阻止事件冒泡
---
### `create_menuitem(item)`
创建单个菜单项 UI 组件(`HBox`)。
#### 参数
- `item`: 菜单项配置对象
| 属性 | 类型 | 说明 |
|------|------|------|
| `label` | String | 显示文本(支持 i18n |
| `icon` | String | 图标 URL |
| `url` | String | 导航链接 |
| `name` | String | 名称(备用标题) |
| 其他字段 | Any | 将直接挂载到返回的 widget 上 |
#### 返回值
- `{bricks.HBox}` 包含图标和文本的水平布局组件
#### 子组件
| 组件 | 类型 | 说明 |
|------|------|------|
| `iw` | `Icon``BlankIcon` | 图标区域,若无图标则占位 |
| `tw` | `Text` | 文本标签,启用自动换行、左对齐、填充样式 |
#### 样式类
- 应用 CSS 类名:`menuitem_css` 或默认 `'menuitem'`
---
### `regen_menuitem_event(item, event)`
菜单项点击事件处理器,用于非容器型菜单项。
#### 行为
- 记录日志
- 触发 `item_click` 事件并传递 `item`
- 阻止事件冒泡
---
## 事件系统
### 监听的事件
- `item_click`: 当菜单项被点击时触发,由 `regen_menuitem_event` 发出
### 分发的事件
| 事件名 | 数据 | 说明 |
|--------|------|------|
| `item_click` | `item` 对象 | 内部使用,通知上级处理点击 |
| `command` | `item` 对象 | 外部可监听,表示一个命令被执行 |
---
## 配置与扩展
### 默认弹窗选项获取
- `bricks.get_popupwindow_default_options()`
- `bricks.get_popup_default_options()`
可通过 `popup_options` 合并自定义配置(使用 `bricks.extend`
### 国际化支持
- `Text` 组件启用 `i18n: true`,支持多语言翻译
### 样式定制
- 可通过 `menuitem_css` 自定义菜单项样式类
- 使用 `set_css()``set_style()` 进行细粒度控制
---
## 注册与工厂模式
```js
bricks.Factory.register('Menu', bricks.Menu);
```
允许通过字符串标识符创建实例:
```js
bricks.createWidget("Menu", options);
```
---
## 使用场景示例
### 场景 1静态树形菜单
```js
const menu = new bricks.Menu({
items: [
{
label: "仪表盘",
icon: "/icons/dashboard.png",
url: "/dashboard"
},
{
label: "设置",
items: [
{ label: "账户", url: "/settings/account" },
{ label: "安全", url: "/settings/security" }
]
}
],
target: "main_panel"
});
```
### 场景 2动态加载子菜单
```js
const menu = new bricks.Menu({
items: [
{
label: "产品分类",
submenu: "/api/categories",
icon: "/icons/folder.png"
}
],
target: "PopupWindow",
popup_options: {
width: 800,
height: 600
}
});
```
---
## 注意事项
1. **性能优化**:动态子菜单仅在首次展开时请求数据,后续展开直接显示缓存内容。
2. **事件隔离**:所有点击事件均调用 `stopPropagation()` 防止意外冒泡。
3. **URL Widget 构建**:依赖 `bricks.widgetBuild()` 异步构建,需确保环境已初始化。
4. **目标组件存在性**:应确保 `target` 所指组件已注册且可用。
---
## 依赖模块
| 模块 | 用途 |
|------|------|
| `bricks.VBox`, `bricks.HBox` | 布局容器 |
| `bricks.Icon`, `bricks.BlankIcon` | 图标显示 |
| `bricks.Text` | 文本渲染与 i18n |
| `bricks.PopupWindow`, `bricks.Popup` | 弹窗组件 |
| `bricks.HttpJson` | JSON 数据请求 |
| `bricks.Factory`, `bricks.widgetBuild` | 组件工厂与异步构建 |
| `bricks.extend` | 对象合并工具函数 |
---
## 版本信息
- **作者**: Bricks Framework Team
- **版本**: v1.0(基础功能稳定)
- **最后更新**: 2025年4月5日
---
✅ *本组件适用于构建灵活、可扩展的菜单系统,推荐结合路由系统或 CMS 后台使用。*

View File

@ -1,218 +0,0 @@
# `bricks.Message``bricks.Error` 技术文档
> 基于 Bricks UI 框架的消息弹窗组件
---
## 概述
`bricks.Message``bricks.Error` 是基于 `bricks.PopupWindow` 构建的轻量级消息提示组件,用于在 Web 应用中显示文本信息或错误提示。该模块提供统一的 API 接口(`bricks.show_message``bricks.show_error`),便于快速调用。
- `bricks.Message`:通用消息弹窗。
- `bricks.Error`:专用于显示错误信息的弹窗,样式上更突出。
- 支持国际化i18n、自动换行、居中对齐等特性。
---
## 继承结构
```
bricks.PopupWindow
└── bricks.Message
└── bricks.Error
```
---
## 类定义
### `bricks.Message`
继承自 `bricks.PopupWindow`,用于创建可配置的消息提示窗口。
#### 构造函数
```js
new bricks.Message(opts)
```
##### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts` | Object | ✅ | 配置选项对象 |
###### `opts` 属性
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `title` | String | - | 弹窗标题 |
| `message` | String | - | 要显示的消息正文内容 |
| `auto_open` | Boolean | `true` | 是否在构造后自动打开窗口(由构造函数内部设置) |
| `cheight` | Number | `9` | 内容区域高度(单位:网格) |
| `cwidth` | Number | `16` | 内容区域宽度(单位:网格) |
> ⚠️ 注意:`auto_open` 在构造函数中被强制设为 `true`,因此所有 `Message` 实例创建后会立即显示。
#### 方法
##### `create_message_widget()`
私有方法,负责构建消息内容区域的 UI 结构。
**UI 结构如下:**
```
Filler (填充容器)
└── VScrollPanel (垂直滚动面板)
└── Text (文本控件,支持换行和居中)
```
- 使用 `bricks.Filler` 作为内容填充层。
- 使用 `bricks.VScrollPanel` 包裹文本以支持长文本滚动。
- 使用 `bricks.Text` 显示消息,启用:
- 自动换行 (`wrap: true`)
- 水平居中对齐 (`halign: 'middle'`)
- 国际化支持 (`i18n: true`)
##### `set_css(cssClass)`
设置弹窗的 CSS 样式类前缀为 `'message'`,用于应用主题样式。
---
### `bricks.Error`
继承自 `bricks.Message`,专用于显示错误信息。
#### 构造函数
```js
new bricks.Error(opts)
```
##### 参数
`bricks.Message``opts`
##### 行为差异
- 调用父类构造函数(即 `super(opts)`)完成初始化。
- 额外调用 `this.set_css('error')`,将样式类切换为 `'error'`,通常表现为红色边框/背景等视觉警示。
---
## 全局快捷函数
### `bricks.show_message(opts)`
快速显示一条普通消息。
```js
bricks.show_message({
title: "提示",
message: "操作已成功完成。"
});
```
#### 参数
`bricks.Message``opts`
> 若未指定 `cheight``cwidth`,则默认使用 `9``16`
---
### `bricks.show_error(opts)`
快速显示一条错误消息。
```js
bricks.show_error({
title: "出错了",
message: "无法连接到服务器,请检查网络。"
});
```
#### 参数
`bricks.Message``opts`
> 视觉样式通过 `set_css('error')` 强化,适合错误场景。
---
## 工厂注册
为支持动态创建机制,两类组件已在 `bricks.Factory` 中注册:
```js
bricks.Factory.register('Message', bricks.Message);
bricks.Factory.register('Error', bricks.Error);
```
这意味着可以通过工厂模式按名称实例化这些组件:
```js
var msg = bricks.Factory.create('Message', { title: "Hello", message: "World" });
```
---
## 使用示例
### 显示普通消息
```js
bricks.show_message({
title: "欢迎",
message: "欢迎使用 Bricks 框架!这是一个示例消息。",
cheight: 10,
cwidth: 20
});
```
### 显示错误消息
```js
bricks.show_error({
title: "加载失败",
message: "请求的数据未能获取,请稍后再试。长时间失败请联系管理员。",
cheight: 12
});
```
---
## 样式说明
| 组件 | CSS Class 前缀 | 典型用途 |
|------|----------------|----------|
| `bricks.Message` | `.message-*` | 一般通知、提示 |
| `bricks.Error` | `.error-*` | 错误、警告信息 |
开发者应在 CSS 中定义相应的样式规则以实现美观的视觉效果。
---
## 依赖项
确保以下组件已加载:
- `bricks.PopupWindow`
- `bricks.Filler`
- `bricks.VScrollPanel`
- `bricks.Text`
- `bricks.Factory`
---
## 版本信息
- 创建时间:未知
- 框架版本Bricks UI假设
- 作者Bricks 团队 / 开发者社区
---
✅ **推荐使用 `bricks.show_message()``bricks.show_error()` 快捷函数进行调用,避免手动管理窗口生命周期。**

View File

@ -1,314 +0,0 @@
# `bricks.MiniForm` 技术文档
> **MiniForm** 是一个基于 `bricks.HBox` 的轻量级表单组件,用于动态切换和输入不同字段的数据。它支持通过下拉选择器快速切换输入项,并实时触发输入事件。
---
## 概述
`bricks.MiniForm` 类继承自 `bricks.HBox`,提供了一个紧凑的表单界面,适用于需要在多个输入字段之间快速切换的场景(如搜索框、参数配置等)。该组件包含:
- 一个下拉选择器(用于选择当前字段)
- 一个动态输入控件(根据所选字段类型自动创建)
- 支持自定义字段属性和默认值
- 可扩展的输入事件机制
---
## 继承关系
```
bricks.HBox
└── bricks.MiniForm
```
---
## 构造函数
```js
new bricks.MiniForm(options)
```
### 参数
| 参数名 | 类型 | 必填 | 描述 |
|-------|------|------|------|
| `options` | Object | ✅ | 配置对象,结构如下 |
#### `options` 结构
| 属性 | 类型 | 必填 | 默认值 | 描述 |
|------|------|------|--------|------|
| `defaultname` | String | ❌ | 第一个字段的 `name` | 默认激活的字段名称 |
| `label_width` | String/Number | ❌ | - | 标签列宽度(可被字段级覆盖) |
| `input_width` | String/Number | ❌ | - | 输入框宽度(可被字段级覆盖) |
| `params` | Object | ❌ | `{}` | 固定附加参数,在 `getValue()` 中返回 |
| `fields` | Array&lt;Object&gt; | ✅ | - | 字段定义数组,每个字段包含以下子属性 |
##### `fields[i]` 字段定义
| 属性 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `name` | String | ✅ | 字段唯一标识符(如:`"username"` |
| `label` | String | ✅ | 显示名称(出现在下拉列表中) |
| `icon` | String | ❌ | 图标类名(可选,用于视觉增强) |
| `uitype` | String | ✅ | 控件类型(如 `"text"`, `"number"`, `"code"` 等),需与 `Input.factory` 兼容 |
| `uiparams` | Object | ❌ | 传递给具体输入控件的额外配置参数 |
---
## 示例配置
```js
var miniForm = new bricks.MiniForm({
defaultname: 'email',
params: { action: 'search' },
fields: [
{
name: 'username',
label: '用户名',
uitype: 'text',
uiparams: { placeholder: '请输入用户名' }
},
{
name: 'email',
label: '邮箱',
uitype: 'text',
uiparams: { type: 'email', placeholder: '请输入邮箱' }
},
{
name: 'count',
label: '数量',
uitype: 'number',
uiparams: { min: 1, max: 100 }
}
]
});
```
---
## 方法说明
### `constructor(opts)`
初始化 MiniForm 实例并调用 `build()`
#### 行为:
- 设置容器宽高为 `'auto'`
- 调用父类构造函数
- 执行 `build()` 构建 UI
---
### `build()`
构建整个组件的 UI包括选项选择器和初始输入控件。
#### 步骤:
1. 确定默认字段名(优先使用 `defaultname`,否则取第一个字段)
2. 调用 `build_options()` 创建下拉选择器
3. 调用 `build_widgets(name)` 初始化对应输入控件
---
### `build_options()`
创建一个下拉选择控件(`this.choose`),允许用户切换当前编辑的字段。
#### 使用 `Input.factory` 创建的选择器配置:
```js
{
width: "90px",
name: "name",
uiType: "code", // 假设为下拉或代码选择器
valueField: 'name', // 绑定值为字段的 name
textField: 'label', // 显示文本为 label
data: this.opts.fields
}
```
> ⚠️ 注:`uiType: "code"` 可能表示一种特殊下拉控件,实际应确保其行为符合预期(例如 ComboBox 或 Select
绑定事件:`changed``change_input(e)`
---
### `build_widgets(name)`
根据指定字段名动态创建对应的输入控件。
#### 参数:
- `name`: 字段名(必须存在于 `fields` 中)
#### 流程:
1. 若已有输入控件,则解绑 `input` 事件
2. 清空当前所有控件
3. 添加选择器控件 `this.choose`
4. 查找匹配字段定义 `f`
5. 复制字段描述并设置 `width: 'auto'`
6. 使用 `Input.factory(desc)` 创建输入控件 `i`
7. 绑定 `input` 事件到 `input_handle`
8. 将新控件加入布局
9. 保存引用至 `this.input`
---
### `change_input(e)`
当下拉选择器值改变时触发,重新构建输入控件。
#### 行为:
- 获取当前选择的字段名 `this.choose.value`
- 调用 `build_widgets(name)` 切换输入控件
---
### `input_handle(e)`
当输入内容发生变化时触发,用于广播事件。
#### 行为:
1. 调用 `getValue()` 获取完整数据
2. 输出调试信息(通过 `bricks.debug`
3. 触发 `input` 事件,携带数据 `d`
```js
this.dispatch('input', d);
```
> 可通过 `.bind('input', handler)` 监听此事件
---
### `getValue() → Object`
获取当前表单的合并值。
#### 返回值结构:
```js
{
// 来自 this.opts.params 的固定参数
action: 'search',
// 来自当前输入控件的动态值
username: 'john_doe'
}
```
#### 逻辑:
- 初始化为 `params` 对象(若未设置则为空 `{}`
- 合并当前输入控件的值(`this.input.getValue()`
- 使用 `bricks.extend(d, v)` 进行浅合并
---
### `show_options(e)`
显示下拉选择器(调试用途或外部调用)。
#### 行为:
- 输出调试日志
- 调用 `this.choose.show()` 显示选择器
> 主要用于开发调试或定制交互逻辑
---
### `clear_widgets()`
> 📌 注意:此方法未在代码中定义,但被调用。
假设其来自父类 `HBox`,功能为清空所有子控件。
---
## 事件系统
### 支持监听的事件
| 事件名 | 触发时机 | 携带数据 |
|--------|----------|---------|
| `input` | 输入值变化时 | 合并后的表单数据对象 |
#### 示例监听:
```js
miniForm.bind('input', function(data) {
console.log('Current form data:', data);
// { action: 'search', username: 'testuser' }
});
```
---
## 工厂注册
```js
bricks.Factory.register('MiniForm', bricks.MiniForm);
```
允许通过工厂模式创建实例:
```js
var form = bricks.Factory.create('MiniForm', options);
```
---
## 设计特点
| 特性 | 说明 |
|------|------|
| ✅ 动态输入切换 | 支持运行时更换输入控件类型 |
| ✅ 模块化结构 | 基于 `Input.factory` 实现控件解耦 |
| ✅ 可扩展性 | 支持任意 `uitype``uiparams` |
| ✅ 事件驱动 | 提供标准 `input` 事件接口 |
| ⚠️ 宽度控制有限 | 当前 `label_width` / `input_width` 未完全实现(可能依赖样式或其他机制) |
---
## 注意事项
1. **`Input.factory` 依赖**
必须确保 `Input.factory` 支持所有声明的 `uitype` 类型。
2. **`objcopy()` 函数**
代码中使用了 `objcopy(f)`,应确保全局存在此工具函数(通常为对象深拷贝或浅拷贝)。
3. **`bricks.extend`**
用于合并对象,类似 jQuery 的 `$.extend` 或 Lodash 的 `_.assign`
4. **UI 渲染顺序**
每次切换字段都会重建输入控件,适合字段较少的场景;高频切换可能影响性能。
5. **样式兼容性**
推荐配合 CSS 设置 `.bricks-miniform` 或相关类以优化布局。
---
## 调试支持
启用调试模式后,会输出以下信息:
- `show_options()` 调用记录
- `input_handle()` 中的实时数据
可通过 `bricks.debug = console.log` 启用。
---
## 总结
`bricks.MiniForm` 是一个简洁高效的动态表单组件,特别适用于:
- 多条件搜索栏
- 快捷参数输入
- 动态配置面板
结合 `bricks` 框架的输入控件体系,能够快速搭建灵活的用户界面。
---
**推荐使用场景**:工具条、查询过滤器、快捷操作面板
🔧 **可优化方向**:缓存输入控件实例、支持更多布局配置、增加验证机制

View File

@ -1,295 +0,0 @@
# `bricks` 模态组件技术文档
> **版本1.0**
> **模块Modal, BaseModal, ModalForm**
本文档描述了 `bricks` 框架中与模态窗口相关的类,包括 `BaseModal``Modal``ModalForm`。这些类用于创建可重用的弹出层Modal组件支持自定义定位、样式、自动关闭、超时关闭等功能。
---
## 目录
- [概述](#概述)
- [核心类结构](#核心类结构)
- [`bricks.BaseModal`](#bricksbasemodal)
- [构造函数参数](#构造函数参数-base-modal)
- [属性](#属性-base-modal)
- [方法](#方法-base-modal)
- [`bricks.Modal`](#bricksmodal)
- [构造函数参数](#构造函数参数-modal)
- [属性](#属性-modal)
- [方法](#方法-modal)
- [`bricks.ModalForm`](#bricksmodalfom)
- [构造函数参数](#构造函数参数-modalfom)
- [属性](#属性-modalfom)
- [方法](#方法-modalfom)
- [注册与使用](#注册与使用)
- [示例代码](#示例代码)
---
## 概述
`bricks` 提供了一套基于布局系统的模态对话框系统:
- `BaseModal` 是所有模态组件的基础类。
- `Modal` 是一个带标题栏和内容区域的标准模态窗口。
- `ModalForm` 是一个用于展示表单的模态窗口,集成 `Form` 组件并处理提交逻辑。
- 所有模态窗口通过 Z-index 管理堆叠顺序,并支持锚点对齐(`archor`)、目标容器绑定等特性。
---
## 核心类结构
```text
bricks.Layout
└── bricks.BaseModal
├── bricks.Modal
└── bricks.ModalForm (extends PopupWindow)
```
> 注意:`ModalForm` 实际继承自 `PopupWindow` 而非 `Modal`,但功能上属于模态系列组件。
---
## `bricks.BaseModal`
基础模态类,提供通用的模态行为,如显示/隐藏、Z-index 控制、目标挂载等。
### 构造函数参数 (Base Modal)
| 参数 | 类型 | 描述 |
|------|------|------|
| `target` | `string``Layout` | 模态窗口挂载的目标容器。字符串表示 ID对象为 Layout 实例。默认为 `bricks.Body`。 |
| `auto_open` | `boolean` | 是否在添加子组件后自动打开模态。 |
| `auto_close` | `boolean` | (未启用)是否点击背景关闭模态。 |
| `org_index` | `number` | 初始 z-index 值(暂未使用)。 |
| `width` | `string` | 内容面板宽度(如 `'500px'`, `'80%'`)。 |
| `height` | `string` | 内容面板高度。 |
| `bgcolor` | `string` | 内容面板背景色,默认 `#e8e8e8`。 |
| `title` | `string` | 标题文本(仅被 `Modal` 使用)。 |
| `timeout` | `number` | 自动关闭延迟时间毫秒0 表示不自动关闭。 |
| `archor` | `string` | 锚点位置,控制内容居中方式:<br>`tl`, `tc`, `tr`, `cl`, `cc`, `cr`, `bl`, `bc`, `br`<br>默认值:`'cc'`(居中) |
### 属性 (Base Modal)
| 属性 | 类型 | 描述 |
|------|------|------|
| `panel` | `VBox` | 实际内容容器,内部垂直布局。 |
| `timeout` | `number` | 自动关闭延时ms。 |
| `timeout_task` | `Task` | 定时任务句柄,用于取消定时关闭。 |
| `target_w` | `Layout` | 解析后的目标挂载容器。 |
| `ancestor_add_widget` | `Function` | 保存父类 `add_widget` 方法的引用。 |
### 方法 (Base Modal)
#### `create()`
创建模态外层 DOM 元素:
- 使用 `<div>` 作为遮罩层。
- 设置 `position: fixed` 遮罩全屏。
- 背景颜色为半透明黑色 (`rgba(0,0,0,0.4)`)。
- 初始隐藏(`display: none`)。
- 自动分配递增的 `z-index`
#### `get_zindex() → number`
获取下一个可用的 `z-index` 值,从 `bricks.min_zindex` 开始递增。
> 默认起始值:`5000`
#### `add_widget(widget, index)`
`panel` 添加子控件。如果设置了 `auto_open: true`,则立即调用 `open()`
#### `open()`
显示模态窗口:
- 将 `dom_element.style.display` 设为空字符串(即显示)。
- 若设置了 `timeout > 0`,启动延迟关闭任务。
- 触发 `opened` 事件。
#### `dismiss()`
关闭并移除模态窗口:
- 隐藏元素。
- 取消超时任务。
- 从父容器中移除自身。
- 触发 `dismissed` 事件。
- 异常捕获防止中断。
---
## `bricks.Modal`
标准模态对话框,继承自 `BaseModal`,带有可关闭标题栏。
### 构造函数参数 (Modal)
`BaseModal`,额外支持:
| 参数 | 类型 | 描述 |
|------|------|------|
| `title` | `string` | 显示在标题栏中的文字。 |
### 属性 (Modal)
| 属性 | 类型 | 描述 |
|------|------|------|
| `title_box` | `HBox` | 水平布局的标题栏容器。 |
| `title_w` | `Filler` | 标题文本占位容器。 |
| `content` | `Filler` | 主体内容容器,填充剩余空间。 |
### 方法 (Modal)
#### `create_title()`
创建标题栏:
- 包含一个水平盒子 `HBox`
- 左侧显示标题文本(使用 `Text` 组件,支持国际化 `i18n` 和动态尺寸 `dynsize`)。
- 右侧放置关闭图标SVG 图标,绑定 `click → dismiss()`)。
- 添加 CSS 类名 `title`
#### `add_widget(widget, index)`
将组件添加到 `content` 区域。若 `auto_open` 为真,则自动打开模态。
#### `click_handler(event)`
点击事件处理器(当前注释状态):
- 如果点击的是模态遮罩层(非内容区域),触发 `dismiss()`
- 用于实现“点击背景关闭”功能(目前未启用)。
> ⚠️ 当前该功能已被注释,需手动启用。
---
## `bricks.ModalForm`
用于快速弹出表单的模态窗口,集成异步表单构建和提交处理。
### 构造函数参数 (ModalForm)
| 参数 | 类型 | 描述 |
|------|------|------|
| `title` | `string` | 表单标题。 |
| `description` | `string` | 表单描述信息。 |
| `fields` | `Array<FieldOptions>` | 表单字段定义数组。 |
| `binds` | `Array<BindConfig>` | 数据绑定配置。 |
| `user_data` | `any` | 用户自定义数据(可选)。 |
| `submit_url` | `string` | 提交 URL可通过实例设置。 |
| 其他 | 同 `PopupWindow` | 如 `width`, `height`, `archor` 等。 |
### 属性 (ModalForm)
| 属性 | 类型 | 描述 |
|------|------|------|
| `form` | `Form` | 动态生成的表单实例。 |
| `submit_url` | `string` | 表单提交地址(可选)。 |
### 方法 (ModalForm)
#### `build_form() → Promise<void>`
异步构建表单:
- 延迟 200ms 执行(确保 DOM 就绪)。
- 使用 `bricks.widgetBuild()` 动态创建 `Form` 组件。
- 添加至模态内容区。
- 绑定以下事件:
- `submit`: 触发 `form_submit`
- `submited`: 转发服务器响应
- `cancel`: 关闭模态
- 最后调用 `open()` 显示。
#### `_getValue() → any`
获取原始表单数据(未经格式化)。
#### `getValue() → any`
获取经过验证和处理的表单数据。
#### `form_submit()`
处理表单提交:
- 获取数据并派发 `submit` 事件。
- 立即关闭模态(`dismiss()`)。
#### `form_submited(event)`
当表单成功提交后,转发 `submited` 事件及其参数。
---
## 注册与使用
```js
// 注册工厂类,便于通过字符串创建
bricks.Factory.register('Modal', bricks.Modal);
bricks.Factory.register('ModalForm', bricks.ModalForm);
```
这意味着你可以使用如下方式动态创建:
```js
let modal = bricks.Factory.create('Modal', { title: '提示', width: '400px' });
```
---
## 示例代码
### 创建一个简单模态窗口
```js
let modal = new bricks.Modal({
title: '欢迎',
width: '500px',
height: '300px',
auto_open: true,
archor: 'cc'
});
modal.add_widget(new bricks.Text({ otext: '这是模态内容!' }));
```
### 创建一个表单模态
```js
let formModal = new bricks.ModalForm({
title: '用户信息',
description: '请填写以下信息',
fields: [
{ name: 'name', label: '姓名', type: 'text' },
{ name: 'age', label: '年龄', type: 'number' }
],
binds: [
['name', 'user.name'],
['age', 'user.age']
]
});
formModal.bind('submit', function(data) {
console.log('提交数据:', data);
});
```
---
## 注意事项
1. **Z-index 管理**:所有模态共享全局 `last_zindex` 计数器,确保新窗口总在最上层。
2. **锚点定位**:依赖外部函数 `archorize(el, pos)` 实现元素定位,请确保其存在。
3. **资源路径**:关闭图标使用 `bricks_resource('imgs/delete.svg')` 加载,需正确配置资源路径。
4. **事件解绑**`click_handler` 的绑定/解绑逻辑目前被注释,如需启用请解除注释并测试。
---
## 依赖说明
- `bricks.Layout`, `bricks.VBox`, `bricks.HBox`, `bricks.Filler`, `bricks.Text`, `bricks.Svg` —— 布局与基础组件。
- `bricks.widgetBuild()` —— 异步组件构建工具。
- `schedule_once(fn, delay)` —— 延迟执行工具函数。
- `objget(obj, key, default)` —— 安全取值工具。
- `archorize(element, position)` —— 锚点定位函数。
---
**建议用途**:适用于消息提示、确认框、登录弹窗、动态表单提交等场景。
🔧 **扩展建议**
- 支持键盘 ESC 关闭。
- 添加动画过渡效果。
- 支持拖拽移动(针对 `ModalForm`)。
---
📖 *文档版本1.0*
📅 *最后更新2025年4月5日*

View File

@ -1,170 +0,0 @@
# `MultipleStateImage` 技术文档
> **命名空间**: `bricks.MultipleStateImage`
> **继承自**: `bricks.Layout`
> **注册名称**: `'MultipleStateImage'`(可通过 `bricks.Factory` 创建)
---
## 概述
`MultipleStateImage` 是一个可交互的图像组件,支持在多个预定义状态之间切换。每个状态对应一张不同的图片 URL。用户点击图像时组件会自动切换到下一个状态并更新显示的图片。
该组件适用于需要通过点击切换不同视觉状态的场景,例如:开关按钮、多状态图标、轮播式图片控件等。
---
## 构造函数
```js
new bricks.MultipleStateImage(options)
```
### 参数
| 参数名 | 类型 | 必填 | 说明 |
|-------|------|------|------|
| `opts` | `Object` | ✅ | 配置选项对象 |
#### `opts` 配置项
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `state` | `String` | ✅ | 初始状态名称,必须是 `urls` 中的一个键 |
| `urls` | `Object<String, String>` | ✅ | 状态与图片 URL 的映射表,格式为 `{ stateName: imageUrl }` |
| `width` | `Number` \| `String` | ❌ | 图像宽度(可选,传递给内部 `bricks.Image` 组件) |
| `height` | `Number` \| `String` | ❌ | 图像高度(可选,传递给内部 `bricks.Image` 组件) |
---
## 示例用法
```js
var image = new bricks.MultipleStateImage({
state: 'off',
urls: {
off: 'images/light_off.png',
on: 'images/light_on.png'
},
width: 100,
height: 100
});
// 添加到父容器
parent.add_widget(image);
// 监听状态变化
image.bind('state_changed', function(newState) {
console.log('当前状态:', newState);
});
```
---
## 方法
### `set_state(state)`
手动设置当前状态并更新图像。
- **参数**:
- `state` (`String`) - 要切换到的状态名,必须存在于 `opts.urls` 中。
- **行为**:
- 更新内部状态 `this.state`
- 调用内部图像的 `set_url()` 方法加载新图片
```js
image.set_state('on');
```
---
### `change_state(event)`
**私有方法 / 事件处理器** — 响应图像点击事件,自动切换到下一个状态。
- **触发方式**: 用户点击图像时自动调用
- **行为**:
- 阻止事件冒泡(`stopPropagation`
- 在 `urls` 的状态列表中循环切换至下一个状态
- 若已是最后一个状态,则回到第一个状态(循环切换)
- 触发 `state_changed` 事件
> ⚠️ 通常无需直接调用此方法,已绑定在图像的 `'click'` 事件上。
---
## 事件
### `state_changed`
当图像状态发生改变时触发。
- **回调参数**:
- `newState` (`String`) - 当前切换后的状态名
```js
image.bind('state_changed', function(state) {
console.log('状态已切换为:', state);
});
```
---
## 内部结构
- 使用 `bricks.Image` 实例作为核心图像渲染组件
- 将自身作为布局容器,通过 `add_widget(this.img)` 管理子组件
- 状态切换逻辑基于 `Object.keys(this.opts.urls)` 的顺序进行循环
---
## 注意事项
1. **状态顺序依赖对象键的枚举顺序**
JavaScript 中对象属性的遍历顺序在现代引擎中通常是插入顺序,建议确保 `urls` 对象的键按预期顺序定义。
2. **URL 合法性需提前校验**
组件不检查图片 URL 是否有效,错误需由外部处理。
3. **事件绑定自动完成**
构造函数中已自动绑定 `'click'` 事件,无需重复绑定。
4. **工厂注册**
已通过 `bricks.Factory.register('MultipleStateImage', ...)` 注册,可在声明式模板中使用类型名创建。
---
## 工厂注册
```js
bricks.Factory.register('MultipleStateImage', bricks.MultipleStateImage);
```
允许通过配置方式创建实例:
```js
bricks.Factory.create({
type: 'MultipleStateImage',
state: 'off',
urls: {
off: 'images/off.png',
on: 'images/on.png'
},
width: 50,
height: 50
});
```
---
## 版权与归属
- 所属模块: `bricks`
- 组件名: `MultipleStateImage`
- 作者: (根据项目上下文补充)
- 版本: 1.0.0
---
✅ *文档结束*

View File

@ -1,117 +0,0 @@
# `Oper` 类技术文档
## 概述
`Oper` 是一个用于封装数值并支持自定义加法操作的 JavaScript 类。通过该类,可以创建包含数值的对象,并使用内置的方法执行对象间的加法运算。
> **注意**:该类中定义了两个功能几乎相同的加法方法 `__plus__``__add__`,可能是为了兼容不同命名习惯或未来扩展预留。
---
## 构造函数
### `constructor(v)`
初始化一个 `Oper` 实例,将传入的值保存在 `value` 属性中。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `v` | `number` | 初始化的数值 |
#### 示例
```javascript
const a = new Oper(5);
console.log(a.value); // 输出: 5
```
---
## 方法
### `__plus__(a, b)`
执行两个 `Oper` 对象的加法操作,返回一个新的 `Oper` 实例,其值为两个操作数 `value` 属性之和。该方法会输出两个操作数到控制台。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `a` | `Oper` | 第一个操作数 |
| `b` | `Oper` | 第二个操作数 |
#### 返回值
- 类型:`Oper`
- 说明:一个新的 `Oper` 实例,其 `value``a.value + b.value`
#### 示例
```javascript
const x = new Oper(3);
const y = new Oper(7);
const result = new Oper().__plus__(x, y);
// 控制台输出: Oper { value: 3 }, Oper { value: 7 }
console.log(result.value); // 输出: 10
```
---
### `__add__(a, b)`
`__plus__` 功能完全相同,执行两个 `Oper` 对象的加法操作并返回新实例,同时打印操作数到控制台。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `a` | `Oper` | 第一个操作数 |
| `b` | `Oper` | 第二个操作数 |
#### 返回值
- 类型:`Oper`
- 说明:一个新的 `Oper` 实例,其 `value``a.value + b.value`
#### 示例
```javascript
const m = new Oper(4);
const n = new Oper(6);
const sum = new Oper().__add__(m, n);
// 控制台输出: Oper { value: 4 }, Oper { value: 6 }
console.log(sum.value); // 输出: 10
```
---
## 注意事项
1. 所有方法均为实例方法,调用时需要先创建 `Oper` 实例。
2. `__plus__``__add__` 行为一致,可能存在冗余,建议根据实际需求保留其一或用于不同语义场景。
3. 当前实现未做类型检查,若传入非 `Oper` 实例或 `value` 非数字,可能导致运行时错误。
4. 方法设计为静态操作风格(接受两个操作数),而非基于当前实例的 `this.value` 进行计算,这可能与常规面向对象设计不符,使用时需注意。
---
## 建议改进
```js
// 改进建议:基于实例的加法
class Oper {
constructor(v) {
this.value = v;
}
add(other) {
return new Oper(this.value + other.value);
}
}
// 使用方式更自然
const a = new Oper(5);
const b = new Oper(3);
const c = a.add(b);
console.log(c.value); // 8
```
---
## 版本信息
- 语言JavaScript (ES6+)
- 兼容性:现代浏览器及 Node.js 环境

View File

@ -1,164 +0,0 @@
# VAD语音活动检测模块技术文档
> **基于 `@ricky0912/vad-web` 的 Web 端语音活动检测集成方案**
---
## 概述
本模块提供了一个轻量级的语音活动检测Voice Activity Detection, VAD功能封装用于在浏览器环境中实时检测用户语音的开始与结束并在语音结束后回调处理音频数据。该模块依赖于 [`vad-web`](https://www.npmjs.com/package/@ricky0123/vad-web) 库,底层使用 ONNX Runtime Web 实现高性能推理。
主要功能:
- 实时麦克风音频采集
- 自动检测语音段Speech Segment
- 在语音结束后触发回调,返回 `Float32Array` 格式的 16kHz 音频样本
- 支持动态启用/禁用 VAD
---
## 依赖引入
```html
<!-- 引入 ONNX Runtime WebVAD 依赖) -->
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
<!-- 引入 vad-web 库 -->
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.7/dist/bundle.min.js"></script>
```
> ⚠️ 注意:必须按顺序加载 `ort.js` 后再加载 `vad-web`,否则会报错。
---
## API 接口
### `bricks.enable_vad(func)`
启动语音活动检测,并设置语音结束时的回调函数。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `func` | `Function` | 回调函数,接收一个参数 `audio`,类型为 `Float32Array`,表示采样率为 16000Hz 的单声道音频数据 |
#### 示例
```javascript
bricks.enable_vad(function(audio) {
console.log("语音结束,音频长度:", audio.length);
// 可将 audio 发送到后端进行 ASR 识别等处理
});
```
#### 内部实现
```javascript
bricks.vad = await vad.MicVAD.new({
onSpeechEnd: func
});
bricks.vad.start();
```
---
### `bricks.disable_vad()`
停止当前运行的 VAD 实例,并释放相关资源。
#### 行为说明
- 调用 `stop()` 方法停止麦克风监听和模型推理
- 将 `bricks.vad` 设置为 `null`,便于垃圾回收
#### 示例
```javascript
// 停止语音检测
bricks.disable_vad();
```
---
## 音频数据格式
- **采样率**16000 Hz
- **位深**32-bit float
- **声道**单声道Mono
- **数据类型**`Float32Array`
- **应用场景**适用于大多数现代语音识别ASR模型输入要求
---
## 使用示例
```html
<!DOCTYPE html>
<html>
<head>
<title>VAD Demo</title>
</head>
<body>
<button onclick="startVAD()">开始语音检测</button>
<button onclick="stopVAD()">停止语音检测</button>
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.7/dist/bundle.min.js"></script>
<script>
var bricks = window.bricks || {};
bricks.enable_vad = async function(func){
bricks.vad = await vad.MicVAD.new({
onSpeechEnd: func
});
bricks.vad.start();
};
bricks.disable_vad = async function(){
if (bricks.vad) {
bricks.vad.stop();
bricks.vad = null;
}
};
function startVAD() {
bricks.enable_vad((audio) => {
console.log("捕获到语音片段,样本数:", audio.length);
// 此处可上传至 ASR 服务或进行本地处理
});
}
function stopVAD() {
bricks.disable_vad();
}
</script>
</body>
</html>
```
---
## 注意事项
1. **浏览器权限**:首次运行时需请求麦克风权限,请确保页面在 HTTPS 环境下运行或本地调试localhost
2. **性能优化**`vad-web` 使用 WASM 和 ONNX 模型进行推理,首次加载可能需要几秒预热时间。
3. **内存管理**:务必在不需要时调用 `disable_vad()` 以释放麦克风资源,避免持续占用。
4. **版本锁定**:当前固定使用 `@ricky0123/vad-web@0.0.7`,建议生产环境保持版本稳定。
---
## 参考链接
- [vad-web GitHub 仓库](https://github.com/ricky0123/vad-web)
- [ONNX Runtime Web](https://onnxruntime.ai/docs/tutorials/web/intro.html)
- [Web Audio API 文档](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)
---
## 版本信息
- **vad-web 版本**`0.0.7`
- **兼容性**支持现代主流浏览器Chrome、Edge、Firefox、Safari
---
📌 **提示**:此模块适合嵌入到低代码平台、语音助手、实时对话系统中,作为前端语音触发机制的核心组件。

View File

@ -1,317 +0,0 @@
# `bricks.PageDataLoader` 技术文档
`PageDataLoader` 是一个用于分页加载数据的 JavaScript 类,支持缓存控制、参数合并、前后翻页等功能。它适用于需要从后端 API 按页获取大量数据的场景,并通过缓存机制优化性能。
---
## 目录
- [概述](#概述)
- [依赖说明](#依赖说明)
- [构造函数与配置选项](#构造函数与配置选项)
- [实例方法](#实例方法)
- [`loadData([params])`](#loaddataparams)
- [`loadNextPage()`](#loadnextpage)
- [`loadPreviousPage()`](#loadpreviouspage)
- [`loadPage(page)`](#loadpagepage)
- [`is_max_page(p)`](#is_max_pagep)
- [返回数据结构](#返回数据结构)
- [使用示例](#使用示例)
- [缓存策略](#缓存策略)
- [注意事项](#注意事项)
---
## 概述
`bricks.PageDataLoader` 提供了一个简洁的接口来管理分页数据的加载和缓存。其主要特性包括:
- 支持自定义请求 URL、分页大小、HTTP 方法等。
- 自动计算总页数。
- 内置页面缓存机制(可配置最大缓存页数)。
- 支持跳转到指定页、下一页、上一页。
- 可扩展的基础参数与动态参数合并。
---
## 依赖说明
该类依赖以下全局对象或模块:
| 依赖 | 说明 |
|------|------|
| `window.bricks` | 全局命名空间对象,提供工具函数 |
| `bricks.extend(obj1, obj2)` | 对象浅拷贝并合并属性(类似 jQuery 的 `$.extend` |
| `bricks.HttpJson()` | 发起 HTTP 请求的封装类,支持 `.httpcall(url, options)` 方法 |
| `bricks.debug(...)` | 调试日志输出函数 |
> ⚠️ 确保这些依赖在运行环境中已正确加载。
---
## 构造函数与配置选项
```js
var loader = new bricks.PageDataLoader(options);
```
### 参数:`options` (Object)
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|---------------|----------|------|--------|------|
| `url` | String | ✅ | - | 数据接口地址 |
| `pagerows` | Number | ❌ | 80 | 每页显示的数据条数 |
| `cache_pages` | Number | ❌ | 5 | 最多缓存多少个页面(超出则移除最远页) |
| `method` | String | ❌ | `'GET'` | HTTP 请求方法(如 `'POST'` |
| `params` | Object | ❌ | `{}` | 基础请求参数(每次请求都会携带) |
### 示例
```js
var p = new bricks.PageDataLoader({
url: '/api/data',
pagerows: 50,
cache_pages: 3,
method: 'GET',
params: {
category: 'news'
}
});
```
---
## 实例方法
### `loadData([params])`
初始化并加载第一页数据。
#### 参数
- `params` (Object, 可选):本次请求附加的参数,会与 `base_params` 合并。
#### 返回值
- Promise解析为第一页的响应数据包含分页信息
#### 行为说明
- 清空当前所有缓存页。
- 合并基础参数与传入参数。
- 调用 `loadPage(1)` 加载第一页。
#### 示例
```js
p.loadData({ search: 'keyword' }).then(data => {
console.log(data);
});
```
---
### `loadNextPage()`
加载下一页数据(即当前已加载页码的最大值 + 1
#### 返回值
- Promise若存在下一页则返回下一页数据否则返回 `undefined`
#### 条件判断
- 仅当目标页未加载且小于等于 `lastPage` 时才发起请求。
#### 示例
```js
p.loadNextPage().then(data => {
if (data) {
render(data.rows); // 渲染新页数据
}
});
```
---
### `loadPreviousPage()`
加载上一页数据(即当前已加载页码的最小值 - 1
#### 返回值
- Promise若存在上一页页码 > 0则返回对应数据否则返回 `undefined`
#### 示例
```js
p.loadPreviousPage().then(data => {
if (data) {
prependToView(data.rows); // 将数据插入视图开头
}
});
```
---
### `loadPage(page)`
加载指定页码的数据(核心方法)。
#### 参数
- `page` (Number):要加载的页码(从 1 开始)。
#### 返回值
- Promise返回该页的完整响应数据。
#### 行为说明
1. 若该页已加载(存在于 `this.pages` 中),不重复请求。
2. 使用 `bricks.HttpJson().httpcall()` 发起请求:
```js
{
page: page,
rows: this.rows
}
```
3. 自动更新 `lastPage`(根据 `total / rows` 计算)。
4. 将当前页加入 `pages` 缓存数组。
5. 若缓存超过 `cache_pages` 数量,则删除“最远”的一页(相对于当前页是首或尾)。
6. 在返回数据中添加元字段(如 `add_page`, `delete_page`, `pos_rate`)。
#### 扩展字段说明
| 字段名 | 类型 | 说明 |
|--------------|--------|------|
| `add_page` | Number | 当前新增的页码 |
| `delete_page`| Number | 因缓存溢出被删除的页码(如有) |
| `pos_rate` | Number | 当前位置占比(用于 UI 定位提示) |
| `last_page` | Number | 总页数(由后端 `total` 推导) |
---
### `is_max_page(p)`
判断某页是否为当前已知的最大页码。
#### 参数
- `p` (Number):待检测的页码。
#### 返回值
- Boolean如果是当前缓存中的最大页码返回 `true`
#### 示例
```js
if (p.is_max_page(5)) {
console.log("这是当前最大的页码");
}
```
---
## 返回数据结构
假设后端返回格式如下:
```json
{
"total": 230,
"rows": [...]
}
```
`PageDataLoader` 会在基础上增加以下字段:
```json
{
"total": 230,
"rows": [...],
"last_page": 3,
"add_page": 1,
"delete_page": 3,
"pos_rate": 0.5
}
```
| 字段 | 说明 |
|--------------|------|
| `last_page` | 根据 `Math.ceil(total / rows)` 得出的总页数 |
| `add_page` | 本次成功加载的页码 |
| `delete_page`| 如果触发了缓存清理,表示被清除的页码 |
| `pos_rate` | 当前页在已有页中的相对位置0 ~ 1便于 UI 定位 |
---
## 使用示例
```js
// 初始化加载器
var loader = new bricks.PageDataLoader({
url: '/api/list',
params: { type: 'article' },
pagerows: 20,
cache_pages: 4,
method: 'GET'
});
// 加载第一页
loader.loadData().then(data => {
displayData(data.rows);
console.log(`共 ${data.last_page} 页`);
});
// 下一页
document.getElementById('next').onclick = () => {
loader.loadNextPage().then(data => {
if (data) appendData(data.rows);
});
};
// 上一页
document.getElementById('prev').onclick = () => {
loader.loadPreviousPage().then(data => {
if (data) prependData(data.rows);
});
};
```
---
## 缓存策略
- 所有已成功加载的页码记录在 `this.pages` 数组中。
- 当 `pages.length > cache_pages` 时,自动删除“最远”的一页:
- 若当前加载的是最后一页 → 删除最早页(最小页码)
- 若当前加载的是较早页 → 删除最后页(最大页码)
> 此策略有助于保持用户浏览路径附近的页数据在内存中。
---
## 注意事项
1. **异步调用**:所有 `loadXxx` 方法均为 `async`,需使用 `await``.then()` 处理结果。
2. **页码从 1 开始**:符合常规分页习惯。
3. **无重复加载**:已加载的页不会再次请求,需手动调用 `loadData()` 重置。
4. **错误处理**:请求失败时会通过 `bricks.debug()` 输出调试信息,但不会抛出异常。
5. **total 必须存在**:后端响应必须包含 `total` 字段,否则无法计算 `lastPage`
---
## 版本信息
- 模块名称:`bricks.PageDataLoader`
- 创建时间:未知
- 维护者:`window.bricks` 团队
> 如需定制行为,建议继承此类或扩展原型方法。
---
**推荐用途**:表格分页、无限滚动、左右滑动浏览历史记录等场景。

View File

@ -1,272 +0,0 @@
# `bricks.BufferedDataLoader` 技术文档
> 一个用于分页加载数据并支持缓冲机制的前端数据加载器。
---
## 概述
`bricks.BufferedDataLoader` 是一个 JavaScript 类,旨在为需要高效处理大量分页数据的 UI 组件(如表格、列表等)提供**带缓冲的数据加载能力**。它通过限制内存中缓存的页面数量来优化性能,并支持向前/向后翻页操作。
该类适用于与支持分页接口的后端服务配合使用,典型场景包括大数据量表格滚动加载、虚拟滚动列表等。
---
## 命名空间
```js
bricks.BufferedDataLoader
```
依赖于全局对象 `window.bricks`,若不存在则自动创建。
---
## 构造函数
### `new bricks.BufferedDataLoader(widget, options)`
#### 参数
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `widget` | Object | ✅ | 数据展示组件实例,必须实现 `.clear_data()`, `.add_rows(rows, direction)`, `.del_old_rows(count, direction)` 方法。 |
| `opts` | Object | ✅ | 配置选项对象,详见下表。 |
#### `opts` 配置项
| 属性 | 类型 | 可选 | 默认值 | 说明 |
|------|------|------|--------|------|
| `url` | String | ❌ | - | 请求数据的 API 地址。 |
| `method` | String | ✅ | `'GET'` | HTTP 请求方法,如 `'GET'``'POST'`。 |
| `params` | Object | ✅ | `{}` | 固定请求参数(例如过滤条件、排序字段等)。 |
| `buffer_pages` | Number | ✅ | `5` | 最大缓存页数,超出时将移除最旧或最新的一页以释放内存。 |
| `pagerows` | Number | ✅ | `60` | 每页返回的数据行数。 |
---
## 实例属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `cur_page` | Number | 当前正在加载/显示的页码(从 1 开始)。 |
| `total_records` | Number | 总记录数(由服务器返回填充)。 |
| `total_page` | Number | 总页数(根据 `total_records / pagerows` 计算得出)。 |
| `buffer` | Object | (预留扩展)当前用于存储已加载页面数据的缓存对象(本版本未实际使用)。 |
| `buffered_pages` | Number | 当前缓冲中的页面数量(影响是否触发旧数据清理)。 |
| `loading` | Boolean | 标志位,表示是否正在进行网络请求。 |
| `direction` | String | 上下方向标识:`'up'` 表示上一页,`'down'` 表示下一页,用于通知 widget 渲染策略。 |
| `cur_params` | Object | 合并后的当前请求参数(包含固定参数和动态传参)。 |
---
## 方法
### `initial()`
重置加载器状态,通常在初始化或重新加载前调用。
#### 功能:
- 重置当前页为 `-1`
- 清空缓冲计数
- 重置总记录数
- 清空当前参数副本
---
### `async loadData(params)`
初始化并加载第一页数据,清空现有数据。
#### 参数:
- `params` *(Object, 可选)*: 动态附加的请求参数(会与 `opts.params` 合并)
#### 返回值:
- `Promise<Object>`: 解析为服务器返回的数据结构,包含:
```js
{
total: 1000,
page: 1,
total_page: 17,
rows: [...]
}
```
#### 流程:
1. 调用 `initial()` 重置状态
2. 清空 widget 显示数据
3. 合并默认参数与传入参数
4. 设置每页行数 (`rows`) 和初始页码 (`page=1`)
5. 调用 `loadPage()` 加载第一页
#### 示例:
```js
const loader = new bricks.BufferedDataLoader(myWidget, {
url: '/api/data',
params: { category: 'A' },
pagerows: 50
});
await loader.loadData({ search: 'keyword' }); // 发送 {category:'A', search:'keyword', rows:50, page:1}
```
---
### `async loadPage(page)`
加载指定页码的数据(内部方法,也可手动调用)。
#### 参数:
- `page` *(Number, 可选)*: 指定要加载的页码,默认为当前 `this.cur_page + 1`(如果未设置)
> ⚠️ 注意:此方法内部管理 `cur_page`,外部应优先使用 `nextPage()` / `previousPage()`
#### 行为逻辑:
1. 若正在加载,则直接返回(防重复提交)
2. 若缓冲页数达到上限(`buffer_pages`),则调用 `widget.del_old_rows()` 删除旧行
3. 构造完整请求参数(含 `page`, `rows` 等)
4. 使用 `bricks.HttpJson` 发起异步请求
5. 更新总记录数和总页数
6. 调用 `widget.add_rows()` 添加新数据
7. 增加缓冲页计数,释放加载锁
#### 返回值:
- `Promise<Object>`: 服务器响应数据
---
### `async nextPage()`
加载下一页数据。
#### 条件判断:
- 如果已在最后一页或正在加载,则不执行任何操作。
#### 行为:
- 设置 `direction = 'down'`
- `cur_page += 1`
- 调用 `loadPage()`
#### 示例:
```js
await loader.nextPage(); // 加载第 2 页
```
---
### `async previousPage()`
加载上一页数据。
#### 条件判断:
- 如果已是第一页或正在加载,则不执行。
#### 行为:
- 设置 `direction = 'up'`
- `cur_page -= 1`
- 调用 `loadPage()`
#### 示例:
```js
await loader.previousPage(); // 回退到前一页
```
---
## widget 接口要求
`BufferedDataLoader` 依赖传入的 `widget` 实现以下三个方法:
| 方法 | 签名 | 用途 |
|------|------|------|
| `.clear_data()` | `function(): void` | 清空当前所有数据显示 |
| `.add_rows(rows, direction)` | `function(Array, String): void` | 添加一批数据行,`direction``'up'``'down'`,可用于决定插入位置 |
| `.del_old_rows(count, direction)` | `function(Number, String): void` | 删除旧数据行(例如顶部或底部若干行),用于维持缓冲大小 |
> 💡 提示这些方法可结合虚拟滚动、DOM 复用等技术实现高性能渲染。
---
## 使用示例
```js
// 定义 widget 对象(模拟组件)
const myWidget = {
data: [],
clear_data() {
this.data = [];
console.log("数据已清空");
},
add_rows(rows, direction) {
if (direction === 'up') {
this.data.unshift(...rows);
} else {
this.data.push(...rows);
}
console.log(`添加了 ${rows.length} 行数据,方向: ${direction}`);
},
del_old_rows(count, direction) {
if (direction === 'up') {
this.data.splice(-count); // 删除末尾
} else {
this.data.splice(0, count); // 删除开头
}
console.log(`删除了 ${count} 行旧数据`);
}
};
// 创建 BufferedDataLoader 实例
const loader = new bricks.BufferedDataLoader(myWidget, {
url: '/api/list',
method: 'POST',
params: { type: 'report' },
buffer_pages: 3,
pagerows: 20
});
// 初始加载第一页
await loader.loadData({ keyword: 'test' });
// 下一页
await loader.nextPage();
// 上一页
await loader.previousPage();
```
---
## 注意事项
1. **线程安全**:通过 `this.loading` 实现简单的并发控制,防止重复请求。
2. **内存控制**:通过 `buffer_pages` 控制最大缓存页数,避免内存溢出。
3. **方向感知**`direction` 字段帮助 widget 区分上下滑动行为,便于优化 UI 渲染。
4. **总页数计算修正**
```js
if (d.total_page * this.pagerows < this.total_record) {
d.total_page += 1;
}
```
此处逻辑存在潜在错误(应使用向上取整),建议改为:
```js
d.total_page = Math.ceil(d.total / this.pagerows);
```
---
## 改进建议
| 问题 | 建议修复 |
|------|---------|
| `total_page` 计算错误 | 改为 `Math.ceil(total / pagerows)` |
| `buffer` 成员未实际使用 | 应实现真正的页面数据缓存以支持快速回退 |
| 缺少错误处理 | 在 `httpcall` 外包裹 `try-catch` 并抛出异常 |
| 不支持取消请求 | 可引入 `AbortController` 实现请求中断 |
---
## 版本信息
- 所属库:`bricks.js`
- 类名:`BufferedDataLoader`
- 创建时间:未知
- 作者:未知
---
📖 **文档生成于2025年4月5日**

View File

@ -1,9 +0,0 @@
# XXX
## 创建选项
## 属性
## 方法
## 事件

View File

@ -1,322 +0,0 @@
# `bricks` 日期工具与组件库技术文档
---
## 概述
`bricks` 是一个轻量级的 JavaScript 工具和 UI 组件库,提供常用的日期处理函数以及可交互的日期周期控件。本文档详细说明其核心功能模块:
- 日期字符串与 `Date` 对象之间的转换
- 日期增减操作(天、月、年)
- `PeriodDays` 可点击切换周期的 UI 控件
该库设计用于前端项目中快速构建时间范围选择器等交互式组件。
---
## 全局命名空间
```js
var bricks = window.bricks || {};
```
> 所有功能均挂载在全局 `bricks` 命名空间下,避免污染全局作用域。
---
## 1. `bricks.str2date(sdate)`
将格式为 `"YYYY-MM-DD"` 的字符串转换为 `Date` 对象。
### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|------------------------|
| `sdate` | string | 格式为 `"YYYY-MM-DD"` 的日期字符串 |
### 返回值
- `{Date}`:对应的 `Date` 对象(注意:月份从 0 开始)
### 示例
```js
bricks.str2date("2025-04-05"); // 返回 Date 对象,表示 2025年4月5日
```
### 注意事项
- 输入字符串必须符合 `YYYY-MM-DD` 格式。
- `new Date(year, month - 1, day)` 中月份需减 1因 JS 的 `Date` 对象月份从 0 起始。
---
## 2. `bricks.date2str(date)`
`Date` 对象格式化为 `"YYYY-MM-DD"` 字符串。
### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|--------------------|
| `date` | Date | 要格式化的日期对象 |
### 返回值
- `{string}`:格式化后的 `"YYYY-MM-DD"` 字符串,不足两位自动补零。
### 示例
```js
const d = new Date(2025, 3, 5); // 2025-04-05
bricks.date2str(d); // "2025-04-05"
```
---
## 3. `bricks.addMonths(dateObj, months)`
在指定日期上增加若干个月。
### 参数
| 参数名 | 类型 | 描述 |
|------------|--------|--------------------------|
| `dateObj` | Date | 原始日期对象 |
| `months` | number | 要增加的月数(可为负) |
### 返回值
- `{Date}`:新的 `Date` 对象,原对象不被修改
### 示例
```js
const d = new Date(2025, 0, 15); // 2025-01-15
bricks.addMonths(d, 3); // 2025-04-15
bricks.addMonths(d, -1); // 2024-12-15
```
---
## 4. `bricks.addYears(dateObj, years)`
在指定日期上增加若干年。
### 参数
| 参数名 | 类型 | 描述 |
|-----------|--------|--------------------------|
| `dateObj` | Date | 原始日期对象 |
| `years` | number | 要增加的年数(可为负) |
### 返回值
- `{Date}`:新的 `Date` 对象
### ⚠️ 注意
使用了 `setYear()` 方法,但请注意:
- `setYear()` 在现代 JS 中已被弃用,推荐使用 `setFullYear()` 替代。
- 当前实现可能存在问题:`getYear()` 返回的是相对于 1900 的偏移量,因此 `+ years` 实际上是错误逻辑!
### ✅ 修正建议
```js
bricks.addYears = function(dateObj, years){
const newDate = new Date(dateObj);
newDate.setFullYear(newDate.getFullYear() + years);
return newDate;
}
```
> 建议尽快替换以确保跨世纪正确性(如 2099 → 2100 年等问题)。
---
## 5. `bricks.addDays(dateObj, days)`
在指定日期上增加若干天。
### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|--------------------------|
| `dateObj`| Date | 原始日期对象 |
| `days` | number | 要增加的天数(可为负) |
### 返回值
- `{Date}`:新的 `Date` 对象
### 示例
```js
const d = new Date(2025, 0, 1);
bricks.addDays(d, 10); // 2025-01-11
bricks.addDays(d, -1); // 2024-12-31
```
---
## 6. `bricks.PeriodDays` —— 周期日期选择控件
一个继承自 `bricks.HBox` 的 UI 组件,允许用户通过点击前后文本切换时间区间。
### 继承关系
```js
class PeriodDays extends bricks.HBox
```
### 配置选项 (`opts`)
| 属性名 | 类型 | 默认值 | 描述 |
|--------------|----------|-------------|------|
| `start_date` | string | 必填 | 起始日期 `"YYYY-MM-DD"` |
| `end_date` | string | 必填 | 结束日期 `"YYYY-MM-DD"` |
| `step_type` | string | `'days'` | 步进类型:`'days'`, `'months'`, `'years'` |
| `step_cnt` | number | `1` | 每次步进的数量 |
| `title` | string | `undefined` | 显示标题(支持国际化) |
| `splitter` | string | `' 至 '` | 分隔符文本,显示在起止日期之间 |
### 支持事件
- **`changed`**:当日期范围发生变化时触发,携带新范围数据。
#### 事件参数结构
```json
{
"start_date": "YYYY-MM-DD",
"end_date": "YYYY-MM-DD"
}
```
### 成员方法
#### `date_add(strdate, step_cnt, step_type)`
内部方法:对字符串日期执行加法运算并返回新字符串。
##### 参数
| 参数名 | 类型 | 描述 |
|--------------|----------|------|
| `strdate` | string | 输入日期字符串 |
| `step_cnt` | number | 步进步数 |
| `step_type` | string | `'days'`, `'months'`, `'years'` |
##### 返回值
- `{string}`:结果日期字符串(格式 `"YYYY-MM-DD"`
##### 示例
```js
this.date_add("2025-01-01", 1, "months"); // "2025-02-01"
```
---
#### `step_back()`
点击起始日期时触发,整体周期向前移动(减去步长)。
- 更新 `start_date``end_date`
- 刷新界面显示
- 触发 `changed` 事件
---
#### `step_forward()`
点击结束日期时触发,整体周期向后移动(加上步长)。
- 同上,方向相反
---
### 用户交互设计
| 元素 | 行为 |
|----------------|------------------------------|
| 起始日期文本 | 点击 → 周期整体前移 |
| 结束日期文本 | 点击 → 周期整体后移 |
> 文本添加了 `clickable` CSS 类,并绑定点击事件。
---
### 使用示例
```js
const period = new bricks.PeriodDays({
start_date: "2025-01-01",
end_date: "2025-01-31",
step_type: "months",
step_cnt: 1,
title: "统计周期",
splitter: " 至 "
});
// 监听变化
period.bind('changed', function(e){
console.log("新周期:", e.start_date, "到", e.end_date);
});
```
渲染效果类似:
```
统计周期 2025-01-01 至 2025-01-31
```
点击 “2025-01-01” → 变为 “2024-12-01 至 2024-12-31”
点击 “2025-01-31” → 变为 “2025-02-01 至 2025-02-28”
---
## 注册组件
```js
bricks.Factory.register('PeriodDays', bricks.PeriodDays);
```
> 使得可通过工厂模式动态创建此组件。
---
## 总结
| 功能 | 是否推荐使用 | 备注 |
|----------------------|--------------|------|
| `str2date` / `date2str` | ✅ 推荐 | 安全可靠 |
| `addDays` / `addMonths` | ✅ 推荐 | 正确封装 |
| `addYears` | ⚠️ 需修复 | 应改用 `setFullYear` |
| `PeriodDays` | ✅ 推荐 | 交互清晰,适合报表周期切换 |
---
## 附录:已知问题与改进建议
1. ❗ `addYears` 使用 `getYear/setYear` 存在兼容性和逻辑错误,应改为:
```js
bricks.addYears = function(dateObj, years) {
const newDate = new Date(dateObj);
newDate.setFullYear(newDate.getFullYear() + years);
return newDate;
};
```
2. ✅ 所有日期操作均创建副本,保护原始对象,良好实践。
3. 🔹 `PeriodDays` 未做边界校验(如非法日期),建议增加输入验证。
4. 🔹 支持更多 `step_type``'weeks'` 可扩展性更强。
---
> 文档版本v1.0
> 最后更新2025年4月5日

View File

@ -1,257 +0,0 @@
# `bricks.ChartPie` 技术文档
## 概述
`bricks.ChartPie` 是基于 `bricks.EchartsExt` 扩展的类,用于创建可配置的饼图组件。它利用 ECharts 作为底层图表引擎,支持通过数据接口动态加载数据,并自动生成饼图及其图例。
该组件适用于需要展示分类占比数据的场景,如统计分析、仪表盘等。
---
## 继承关系
```js
class ChartPie extends bricks.EchartsExt
```
继承自 `bricks.EchartsExt`,具备基础的图表初始化、数据获取和事件处理能力。
---
## 构造函数
### `constructor(opts)`
#### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `opts` | Object | 配置选项对象,传递给父类及内部逻辑使用 |
#### 示例
```js
var pieChart = new bricks.ChartPie({
nameField: 'category',
valueFields: ['sales'],
data_url: '/api/pie-data',
pie_options: {
type: 'pie',
radius: '70%'
},
legend: {
show: true,
orient: 'vertical'
}
});
```
> ⚠️ 注意:构造函数中调用了 `super(opts)`,因此所有父类支持的配置项也适用。
---
## 配置选项Options
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|----------------|----------|------|--------|------|
| `title` | String | 否 | - | 图表标题 |
| `description` | String | 否 | - | 图表描述信息(可用于辅助提示) |
| `legend` | Object | 否 | `{}` | 图例配置项,将直接传入 ECharts 的 `legend` 配置 |
| `pie_options` | Object | 是 | - | 饼图系列的核心配置,如 `type`, `radius`, `center` 等 |
| `data_url` | String | 否 | - | 数据接口 URL若提供则自动异步加载数据 |
| `nameField` | String | 是 | - | 指定哪一字段作为饼图扇区的名称(即类别) |
| `valueFields` | Array | 是 | - | 数组形式指定值字段(目前仅取第一个字段用于数值) |
| `data_params` | Object | 否 | - | 请求 `data_url` 时携带的额外参数 |
| `data` | Array | 否 | `[]` | 直接传入的静态数据数组 |
---
## 核心方法
### `setup_options(data)`
根据传入的数据生成符合 ECharts 要求的图表配置项。
#### 参数
| 参数名 | 类型 | 说明 |
|-------|-------|------|
| `data` | Array | 原始数据数组,通常来自 API 或静态赋值 |
#### 返回值
返回一个符合 ECharts schema 的配置对象,结构如下:
```js
{
legend: { ... }, // 图例配置
tooltip: {
trigger: 'item' // 鼠标悬停显示单项详情
},
series: [
{ /* 处理后的饼图系列配置 */ }
]
}
```
#### 内部逻辑说明
1. **数据映射**
- 使用 `this.nameField` 映射为每个扇区的 `name`
- 使用 `this.valueFields[0]` 映射为 `value`
- 构建新的数据格式:`{ name: '类别A', value: 100 }`
2. **合并饼图配置**
- 使用 `bricks.extend({}, this.pie_options)` 深拷贝避免污染原始配置
- 将处理后的数据注入到 `series[0].data`
3. **图例处理**
- 若设置了 `legend`,则保留其配置
- 注释代码表明原计划支持多字段图例,当前未启用
#### 示例输出
假设输入数据为:
```js
[
{ category: "手机", sales: 60 },
{ category: "平板", sales: 40 }
]
```
且配置为:
```js
{
nameField: 'category',
valueFields: ['sales'],
pie_options: { type: 'pie', radius: '80%' }
}
```
`setup_options` 输出为:
```js
{
legend: {},
tooltip: { trigger: 'item' },
series: [{
type: 'pie',
radius: '80%',
data: [
{ name: '手机', value: 60 },
{ name: '平板', value: 40 }
]
}]
}
```
---
## 支持的事件
### `element_click`
当用户点击饼图中的某个扇区时触发。
#### 回调参数
ECharts 提供的标准事件参数对象,包含:
- `name`: 扇区名称
- `value`: 对应数值
- `data`: 原始数据项
- `seriesName`: 系列名称(如有)
#### 使用方式
```js
pieChart.on('element_click', function(e) {
console.log('Clicked:', e.name, e.value);
});
```
---
## 注册与使用
### 注册组件
```js
bricks.Factory.register('ChartPie', bricks.ChartPie);
```
通过工厂模式注册,后续可通过字符串标识符创建实例:
```js
var chart = bricks.Factory.create('ChartPie', options);
```
---
## 完整示例
```js
// 初始化饼图
var myPie = new bricks.ChartPie({
nameField: 'product',
valueFields: ['revenue'],
data_url: '/report/sales-by-product',
data_params: { year: 2024 },
pie_options: {
type: 'pie',
radius: ['50%', '70%'],
label: {
show: true,
formatter: '{b}: {d}%'
}
},
legend: {
show: true,
orient: 'horizontal',
bottom: '0%'
},
title: '2024 年产品收入分布'
});
// 渲染到 DOM 元素
myPie.renderTo('#chart-container');
// 绑定点击事件
myPie.on('element_click', function(e) {
alert(`您点击了:${e.name}`);
});
```
---
## 注意事项
1. **`valueFields` 仅支持单个值字段**
当前实现只取数组的第一个字段作为数值来源,不支持多维度饼图。
2. **数据格式要求**
从接口或手动传入的数据必须是对象数组,且包含 `nameField``valueFields[0]` 字段。
3. **依赖 `bricks.extend` 工具函数**
用于安全拷贝 `pie_options`,确保不影响原始配置。
4. **需提前引入 ECharts**
本组件依赖全局 ECharts 库,请确保页面已正确加载。
---
## 版本信息
- 创建时间:未知
- 最后更新:根据代码注释推断近期维护中
- 所属框架:`bricks.js` 可视化组件库
---
**建议扩展方向**
- 支持多层环形图(嵌套饼图)
- 添加动画配置开关
- 支持主题颜色自定义
- 增加空数据状态提示

View File

@ -1,502 +0,0 @@
# Bricks UI Framework - Popup 组件技术文档
---
## 概述
`bricks.Popup` 是基于 `bricks.VBox` 的弹窗组件,用于创建可浮动、可移动、可调整大小的模态或非模态对话框。它支持自动打开/关闭、点击外部区域自动关闭、内容动态加载等功能。
此外,框架还提供了两个扩展类:
- `bricks.PopupWindow`:带标题栏和控制按钮(最小化、全屏、关闭)的窗口式弹窗。
- `bricks.WindowsPanel`:用于管理多个最小化的窗口列表面板。
该组件适用于构建现代化 Web 应用中的对话框、提示框、应用窗口等交互界面。
---
## 全局命名空间初始化
```js
var bricks = window.bricks || {};
```
确保 `bricks` 命名空间存在,避免覆盖已有对象。
---
## 默认配置方法
### `bricks.get_popup_default_options()`
返回一个包含默认选项的对象,供 `Popup` 使用。
#### 返回值
```js
{
timeout: 0,
archor: 'cc',
auto_open: true,
auto_dismiss: true,
auto_destroy: true,
movable: true,
resizable: false,
modal: true
}
```
| 参数 | 类型 | 描述 |
|------|------|------|
| `timeout` | Number | 自动关闭延迟时间(秒),设为 0 表示不自动关闭 |
| `archor` | String | 定位锚点,取值见下表 |
| `auto_open` | Boolean | 是否在实例化后自动打开弹窗 |
| `auto_dismiss` | Boolean | 是否允许点击外部区域关闭弹窗 |
| `auto_destroy` | Boolean | 关闭后是否自动销毁组件 |
| `movable` | Boolean | 是否可拖动 |
| `resizable` | Boolean | 是否可调整大小 |
| `modal` | Boolean | 是否为模态(遮罩其他控件) |
> **`archor` 取值说明**
> 支持九宫格定位:
> `'tl'`, `'tc'`, `'tr'`, `'cl'`, `'cc'`, `'cr'`, `'bl'`, `'bc'`, `'br'`
> 分别表示:左上、中上、右上、左中、居中、右中、左下、中下、右下
---
## 核心类:`bricks.Popup`
继承自 `bricks.VBox`,提供完整的弹窗功能。
### 构造函数:`constructor(opts)`
#### 参数
- `opts` (Object): 配置选项,合并默认配置使用。
#### 内部属性初始化
| 属性 | 类型 | 初始值 | 说明 |
|------|------|--------|------|
| `no_opened` | Boolean | `true` | 是否首次打开 |
| `auto_task` | Task | `null` | 超时任务句柄 |
| `issub` | Boolean | `false` | 是否依附于某个控件显示 |
| `opened` | Boolean | `false` | 当前是否已打开 |
| `content_box` | VBox | 实例 | 内容容器 |
| `content_w` | Widget | `content_box` | 内容承载部件 |
| `moving_w` | Widget | `this` | 可拖动目标(默认整个弹窗) |
| `target_w` | Widget | `bricks.Body` | 目标绑定控件 |
| `moving_status` | Boolean | `false` | 拖拽状态标志 |
| `resize_status` | Boolean | `false` | 缩放状态标志 |
#### 初始化流程
1. 设置 CSS 类名为 `popup`
2. 将自身置于顶层z-index
3. 创建内容容器并添加到内部
4. 绑定点击外部关闭事件(若启用 `auto_dismiss`
5. 启用拖动(`movable`)与缩放(`resizable`
6. 默认隐藏(`display: none`
7. 添加至 `Body` 容器
8. 绑定点击以提升层级
9. 若 `auto_open === true`,调用 `open()`
10. 若有 `content`,监听 `opened` 事件加载内容
---
### 方法详解
#### `load_content() → Promise<void>`
异步加载配置中的 `content` 并渲染到内容区。
```js
async load_content(){
var w = await bricks.widgetBuild(this.content, this);
if (w){
this.set_dismiss_events(w);
this.content_w.clear_widgets();
this.content_w.add_widget(w);
}
}
```
> ⚠️ 依赖 `bricks.widgetBuild()` 动态构建子控件树。
---
#### `set_dismiss_events(widget)`
为指定控件绑定关闭事件。
```js
this.dismiss_events.forEach(ename => {
w.bind(ename, this.destroy.bind(this));
});
```
> 示例:`dismiss_events: ['ok', 'cancel']` 会绑定这些事件触发销毁。
---
#### `bring_to_top()`
将当前弹窗置顶(最高 z-index更新全局 `toppopup` 引用。
```js
this.zindex = bricks.app.new_zindex();
this.set_style('zIndex', this.zindex);
bricks.app.toppopup = this;
```
---
#### `popup_from_widget(from_w)`
从指定控件位置智能定位弹窗(避开屏幕边缘)。
##### 算法逻辑
- 计算源控件中心坐标 `(ox, oy)`
- 若源在左侧,则弹窗出现在右侧;否则出现在左侧
- 若源在上方,则弹窗出现在下方;否则出现在上方
- 边界检测防止溢出屏幕
> 适用于 Tooltip 或 Context Menu 场景。
---
#### `setup_resizable()`
启用可缩放功能,在右下角添加 SVG 缩放手柄。
##### 子组件
- `resizable_w`: SVG 图标(右下三角)
##### 事件绑定
- `mousedown``resize_start_pos`
- `mousemove``resizing`
- `mouseup``stop_resizing`
---
#### `resize_start_pos(e)`
记录初始尺寸和鼠标位置,开始缩放。
```js
this.s_width = rect.width;
this.s_height = rect.height;
this.s_offsetX = e.clientX;
this.s_offsetY = e.clientY;
```
---
#### `resizing(e)`
实时调整宽度和高度。
```js
cx = this.s_width + e.clientX - this.s_offsetX;
cy = this.s_height + e.clientY - this.s_offsetY;
this.set_style('width', cx + 'px');
this.set_style('height', cy + 'px');
```
---
#### `stop_resizing(e)`
结束缩放操作,解绑全局事件。
```js
this.resize_status = false;
bricks.Body.unbind('mousemove', this.resizing.bind(this));
bricks.Body.unbind('mouseup', this.stop_resizing.bind(this));
```
---
#### `positify_tl()`
根据 `archor` 和屏幕尺寸计算并设置弹窗位置。
##### 尺寸解析优先级
1. `cwidth` / `cheight`(字符单位 × 字符大小)
2. `width` / `height`(百分比或 px
3. 默认占屏 80%
##### 锚点布局规则
| 第一位(垂直) | 第二位(水平) |
|----------------|----------------|
| `t`: 顶部对齐 | `l`: 左对齐 |
| `c`: 居中 | `c`: 居中 |
| `b`: 底部对齐 | `r`: 右对齐 |
> 返回 `{ top, left }` 像素坐标。
---
#### `setup_movable()`
启用拖动功能,绑定 `mousedown``touchstart`
---
#### `rec_start_pos(e)`
记录拖动起点偏移量,并绑定移动与释放事件。
```js
this.offsetX = e.clientX - rect.left;
this.offsetY = e.clientY - rect.top;
```
---
#### `moving(e)`
实时更新位置。
```js
cx = e.clientX - this.offsetX;
cy = e.clientY - this.offsetY;
this.set_style('left', cx + 'px');
this.set_style('top', cy + 'px');
```
---
#### `stop_moving(e)`
停止拖动,解绑所有相关事件。
---
#### `click_outside(event)`
点击弹窗外区域时关闭弹窗(仅当 `auto_dismiss``true` 时生效)。
```js
if (event.target != this.dom_element) this.dismiss();
```
---
#### `open()`
打开弹窗。
##### 流程
1. 若无父节点,加入 `app` 根容器
2. 若是首次打开:
- 设置目标控件(`widget` 参数)
- 执行定位 `positify_tl()`
3. 显示元素(`display: block`
4. 触发 `opened` 事件
5. 设置超时关闭任务(如需)
6. 若为模态,则禁用目标控件
7. 提升至顶层
---
#### `dismiss()`
关闭弹窗。
##### 流程
1. 若为模态,恢复目标控件可用状态
2. 清除超时任务
3. 隐藏元素(`display: none`
4. 触发 `dismissed` 事件
5. 若 `auto_destroy === true`,执行 `destroy()`
---
#### `destroy()`
彻底销毁组件。
```js
if (this.opened) this.dismiss();
if (this.parent) {
this.parent.remove_widget(this);
this.parent = null;
}
```
---
#### `add_widget(w, i)`
向内容区域添加控件,并自动绑定关闭事件。
> 若 `auto_open``true`,则立即打开弹窗。
---
#### `remove_widget(w)` / `clear_widgets()`
代理调用 `content_w` 的对应方法。
---
## 扩展类:`bricks.PopupWindow`
继承自 `Popup`,模拟操作系统窗口,带有标题栏和控制按钮。
### 构造函数特点
```js
opts.movable = true;
opts.resizable = true;
opts.auto_open = false; // 手动控制打开时机
```
#### 特有属性
| 属性 | 说明 |
|------|------|
| `title_bar` | 标题栏容器HBox |
| `tb_w` | 控制按钮栏IconBar |
| `content_w` | 主内容区Layout |
| `title_w` | 标题文本显示控件 |
#### 内部结构
1. **标题栏 (`titlebar`)**
- 图标SVG
- 控制按钮组(删除、最小化、全屏)
- 标题文本
2. **填充层 (`Filler`)**
- 包含主内容区
3. **内容区 (`Layout`)**
- 支持 Flex 布局
#### 控制按钮功能
| 按钮 | 功能 |
|------|------|
| `delete` | 调用 `destroy()` 销毁窗口 |
| `minimize` | 调用 `win_minimize()` 最小化 |
| `fullscreen` | 进入全屏模式 |
---
#### `win_minimize()`
关闭窗口并将自身推入 `bricks.app.mwins` 数组,供后续恢复。
```js
this.dismiss();
if (!this.auto_destroy) {
bricks.app.mwins.push(this);
}
```
---
#### `set_title(txt)`
动态修改窗口标题。
```js
this.title_w.set_text(txt);
```
---
## 工具类:`bricks.WindowsPanel`
用于展示所有最小化的窗口列表,点击可恢复。
### 构造函数行为
- 固定宽高为 `80%`
- 自动关闭、销毁
- 不自动打开
#### 数据绑定结构
使用 `Cols` 组件渲染表格:
```json
{
"total": 3,
"rows": [
{ "title": "Settings", "url": "...", "pos": 0 },
...
]
}
```
每行包含图标、标题,点击触发 `del_window()`
---
#### `del_window(event)`
恢复指定位置的窗口并从列表中移除。
```js
var pos = event.params.pos;
var w = bricks.app.mwins[pos];
w.open(); // 恢复显示
bricks.app.mwins.splice(pos, 1); // 删除记录
this.dismiss(); // 关闭面板
```
---
## 工厂注册
```js
bricks.Factory.register('Popup', bricks.Popup);
bricks.Factory.register('PopupWindow', bricks.PopupWindow);
```
支持通过字符串类型动态创建组件实例。
---
## 静态辅助函数
### `bricks.get_popupwindow_default_options()`
返回专用于 `PopupWindow` 的默认配置,与普通 `Popup` 的区别在于:
```diff
+ resizable: true
```
其余同 `get_popup_default_options()`
---
## 使用示例
### 创建简单弹窗
```js
let popup = new bricks.Popup({
content: {
widgettype: 'Text',
options: { text: 'Hello World!' }
},
archor: 'cc',
timeout: 3000
});
```
### 创建应用窗口
```js
let win = new bricks.PopupWindow({
title: '用户设置',
icon: '/icons/settings.svg',
content: { ... },
auto_open: true
});
```
### 显示窗口管理器
```js
let panel = new bricks.WindowsPanel();
panel.open();
```
---
## 注意事项
1. **事件作用域问题**:大量使用 `.bind(this)`,注意性能影响。
2. **内存泄漏风险**:未正确解绑事件可能导致残留监听器。
3. **触摸设备兼容性**:支持 `touchstart/move/end`,但需测试跨平台表现。
4. **z-index 管理**:依赖 `bricks.app.new_zindex()`,需保证唯一递增。
5. **异步内容加载**`load_content()` 为异步,注意加载顺序。
---
## 总结
`bricks.Popup` 系列组件提供了强大而灵活的弹窗系统,具备以下核心能力:
✅ 模态/非模态切换
✅ 自动定位与边界适配
✅ 拖拽移动 & 缩放
✅ 外部点击关闭
✅ 超时自动关闭
✅ 内容动态加载
✅ 窗口最小化管理
适合构建复杂的桌面风格 Web 应用界面。
---
> 文档版本v1.0
> 最后更新2025-04-05

View File

@ -1,150 +0,0 @@
# `bricks.ProgressBar` 技术文档
```markdown
# bricks.ProgressBar
`bricks.ProgressBar` 是一个基于 `bricks.HBox` 的进度条组件,用于在用户界面中可视化显示任务的完成进度。它通过 CSS 样式控制外观,并支持动态设置当前进度值。
---
## 继承关系
- **继承自**: `bricks.HBox`
- **注册名称**: `'ProgressBar'`(通过 `bricks.Factory` 注册)
---
## 构造函数
### `new ProgressBar(opts)`
创建一个进度条实例。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项对象 |
##### 可选配置项 (`opts`)
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `total_value` | Number | `100`(隐式) | 进度条的总值,用于计算百分比 |
| `bar_cwidth` | Number | `2` | 进度条的高度(以字符行为单位),影响内部文本控件的高度 |
> ⚠️ 注意:`total_value` 在当前代码中未被实际读取或存储,逻辑上应传入并在 `set_value` 中使用。
#### 实现细节
- 调用父类构造函数 `super(opts)`
- 设置容器的 CSS 类名为 `progress-container`
- 创建一个 `bricks.Text` 实例作为进度条填充部分:
- 初始文本为 `'0%'`
- 高度由 `bar_cwidth` 决定(默认为 2 行高)
- 添加 CSS 类 `progress-bar`
- 将该文本控件添加到布局中。
---
## 方法
### `set_value(v)`
设置当前进度值并更新进度条的视觉表现。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `v` | Number | 当前进度值(应小于等于 `total_value` |
#### 实现逻辑
> ❗ 存在 Bug以下变量未定义或命名错误
```js
var pzt = (current / total) * 100;
pzt = Math.max(0, Math.min(100, percentage));
```
- `current``total` 未声明。
- `percentage` 变量不存在,应为 `pzt`
- 正确逻辑应基于输入参数 `v` 和预设的 `total_value` 计算百分比。
**建议修正版本**
```js
set_value(v) {
const total = this.total_value || 100;
let percentage = (v / total) * 100;
percentage = Math.max(0, Math.min(100, percentage));
this.text_w.set_style('width', percentage + '%');
}
```
此外,应在构造函数中保存 `this.total_value = opts.total_value || 100;`
---
## 事件
当前组件未定义任何自定义事件。
---
## 样式类
| CSS 类名 | 应用元素 | 用途 |
|---------|----------|------|
| `progress-container` | 容器HBox | 包裹整个进度条的外层样式 |
| `progress-bar` | 文本控件(填充条) | 控制进度条填充部分的样式,通常配合 `width` 动态调整 |
> 建议在 CSS 中定义 `.progress-bar` 为块级元素,具有背景色、过渡动画等效果。
---
## 使用示例
```js
const progressBar = new bricks.ProgressBar({
total_value: 200,
bar_cwidth: 3
});
// 添加到某个容器
someContainer.add_widget(progressBar);
// 更新进度
progressBar.set_value(50); // 显示 25%
progressBar.set_value(100); // 显示 50%
```
---
## 已知问题与改进建议
1. ✅ **Bug**: `set_value` 方法中使用了未定义的变量 `current`, `total`, `percentage`
2. ✅ **缺少属性初始化**`total_value` 应在构造函数中保存。
3. 🔧 **功能扩展建议**
- 支持显示百分比数字文本。
- 提供 `get_value()` 方法。
- 支持事件通知(如 `onchange`)。
4. 🎨 **样式建议**
- 使用 Flexbox 布局确保进度条拉伸一致。
- 添加边框和圆角提升视觉体验。
---
## 注册信息
通过工厂注册,可在模板或动态创建中使用字符串标识:
```js
bricks.Factory.register('ProgressBar', bricks.ProgressBar);
```
---
> **备注**:请修复 `set_value` 中的变量引用错误以确保组件正常工作。
```

View File

@ -1,343 +0,0 @@
# `bricks.QAFrame` 技术文档
> 一个基于 WebSocket 的问答交互框架组件,支持多媒体课件展示、题目呈现与用户答案提交。
---
## 概述
`bricks.QAFrame``bricks` UI 框架中的一个复合组件类,继承自 `bricks.VBox`。它用于构建一个完整的**问答互动界面**,通过 WebSocket 与后端服务通信,支持以下功能:
- 展示多媒体课件视频、音频、图片、Markdown
- 显示问题并接收用户输入(文本或语音)
- 控制答题流程(开始确认、题目切换、结果反馈等)
该组件广泛适用于在线教育、智能测评和互动学习场景。
---
## 继承关系
```
bricks.QAFrame → bricks.VBox → bricks.Widget
```
---
## 构造函数
```js
new bricks.QAFrame(options)
```
### 参数:`options` (Object)
| 属性名 | 类型 | 必填 | 描述 |
|----------------|----------|------|------|
| `ws_url` | String | 是 | WebSocket 连接地址 |
| `ws_params` | Object | 否 | WebSocket 查询参数对象,将被序列化为 URL 查询字符串 |
| `title` | String | 否 | 页面标题(未在代码中使用,保留字段) |
| `description` | String | 否 | 描述信息(未在代码中使用,保留字段) |
| `courseware` | Object | 否 | 初始课件配置,结构见下文 |
#### `courseware` 子属性
| 属性名 | 类型 | 可选值 | 描述 |
|-----------|----------|----------------------------|------|
| `type` | String | `"audio"`, `"video"`, `"image"`, `"markdown"` | 媒体类型 |
| `url` | String | - | 资源 URL |
| `timeout` | Number | 秒数0 表示无超时 | 自动跳转时间 |
> 示例:
> ```js
> courseware: {
> type: 'video',
> url: '/media/intro.mp4',
> timeout: 30
> }
> ```
---
## 内部结构布局
`QAFrame` 使用垂直布局容器VBox包含三个主要区域
| 区域 | 组件 | 功能说明 |
|-----------|---------------|----------|
| `top_w` | `HBox` | 顶部工具栏区域(可显示题号) |
| `main_w` | `Filler` | 主内容区,动态加载课件或问题 |
| `bottom_w`| `HBox` | 底部操作区,放置按钮或输入控件 |
---
## WebSocket 通信协议
`QAFrame` 通过内置的 `bricks.WebSocket` 实例与服务器进行双向通信。
### WebSocket 初始化
自动拼接 `ws_params` 成查询字符串:
```js
const url = this.ws_url + '?' + new URLSearchParams(this.ws_params).toString();
this.ws = new bricks.WebSocket({ ws_url: url });
```
### 监听事件绑定
| 事件名 | 回调方法 | 触发时机 |
|------------------|----------------------|---------|
| `onopen` | `start_question_answer()` | WebSocket 连接建立时 |
| `onquestion` | `show_question(d)` | 收到新问题数据 |
| `oncourseware` | `show_courseware(d)` | 收到课件数据 |
| `onaskstart` | `show_conform(d)` | 请求用户确认开始 |
> ⚠️ 注意:`oncourseware` 被重复绑定三次 —— 此为冗余代码,建议优化。
---
## 接收消息格式Server → Client
由 WebSocket 推送的消息通过 `data` 字段传递,`type` 标识消息类型。
| 编号 | 类型 | `data` 结构 | 说明 |
|------|------------------|-------------|------|
| 1 | `courseware` | `{ type, url }` | 播放指定类型的媒体资源 |
| 2 | `askready` | `{ total_q, cur_q }` | 提示准备开始第几题 |
| 3 | `question` | `{ q_desc, total_q, cur_q }` | 显示具体问题 |
| 4 | `result` | `{ total_q, correct_cnt, error_cnt }` | 显示答题结果统计 |
| 5 | `error_list` | `{ error_cnt, rows: [...] }` | 错误详情列表 |
### `error_list.rows[]` 元素结构
| 字段 | 类型 | 说明 |
|-------------|--------|------|
| `pos` | Number | 题目位置索引 |
| `q_desc` | String | 题干描述 |
| `your_a` | String | 用户答案 |
| `corrent_a` | String | 正确答案 |
| `error_desc`| String | 错误原因说明 |
---
## 发送消息格式Client → Server
| 编号 | 类型 | `data` 值 | 触发方式 |
|------|-------------------|-----------|----------|
| 1 | `qa_start` | `null``{ d: "test data", v: 100 }` | 点击“开始”或连接打开时自动发送 |
| 2 | `conform_start` | `null` | 用户点击“Start?”按钮触发 |
| 3 | `text_answer` | `{ texta: "用户输入内容" }` | 文本输入框失去焦点时 |
| 4 | `audio_answer` | Base64 编码的音频 Blob | 录音结束时自动发送 |
> 示例:
> ```js
> { type: 'text_answer', data: 'Hello world' }
> { type: 'audio_answer', data: 'base64:...' }
> ```
---
## 核心方法说明
### `show_question(d)`
显示一道新问题。
#### 参数
- `d`: `{ q_desc, total_q, cur_q }`
#### 行为
- 更新顶部题号显示(需确保已创建 `qtotal_w``qcur_w`,当前代码缺失定义)
- 使用 `widgetBuild` 解析 `q_desc` 内容生成子组件
- 添加至主区域
> ✅ 支持富内容渲染(如 HTML、自定义组件
---
### `show_courseware(d)`
播放指定课件资源。
#### 参数
- `d`: `{ type, url }`
#### 支持类型
| 类型 | 组件 | 特性 |
|------------|--------------------|------|
| `video` | `bricks.Video` | 全屏自动播放 |
| `audio` | `bricks.AudioPlayer` | 自动播放 |
| `image` | `bricks.Image` | 全屏展示 |
| `markdown` | `bricks.MdWidget` | 加载远程 `.md` 文件 |
> 所有组件均设置宽高为 `100%`,适应容器。
---
### `show_conform(d)`
显示“是否开始”确认按钮。
#### 行为
- 清空主区域
- 添加一个标签为 `"Start ?"` 的按钮
- 绑定点击事件调用 `start_question_answer()`
---
### `start_question_answer()`
向服务端发送启动信号。
```js
this.ws.send({
type: 'qa_start',
data: { d: 'test data', v: 100 }
});
```
通常由 `WebSocket.onopen` 或用户点击触发。
---
### `conform_start()`
发送用户确认开始指令。
```js
this.ws.send({
type: 'conform_start',
data: null
});
```
---
### `send_text_answer(e)`
处理文本答案提交。
#### 参数
- `e.data.texta`: 用户输入文本
#### 流程
- 获取输入值
- 发送 `text_answer` 消息
> 绑定于 `StrInput``blur` 事件(失焦即提交)
---
### `send_audio_answer(e)`
异步处理录音答案提交。
#### 参数
- `e.data.audio`: Blob 音频数据
#### 流程
1. 将 Blob 转为 Base64 字符串(依赖 `blobToBase64` 函数)
2. 发送 `audio_answer` 消息
> ⚠️ 注意:`send_audio_data` 方法未定义,应为笔误,实际应为 `send_audio_answer`
---
### `build_input_widgets()`
构建底部输入控件区。
#### 创建组件
- `StrInput`: 文本输入框name=`texta`,样式填充)
- `AudioRecorder`: 语音录制按钮(带图标动画)
#### 事件绑定
- 文本框失焦 → `send_text_answer`
- 录音结束 → `send_audio_answer`(原写错为 `send_audio_data`
> ❗ 注释部分包含图像/视频采集功能预留接口SVG 图标),目前未启用。
---
### `play_course()`
【未使用】根据 `this.courseware` 播放初始课程内容。
> 当前逻辑中未调用此方法。若需启用,应在构造函数中补充调用。
---
### `build_startbtn()`
构建底部“press to start”按钮。
> 当前未在构造函数中调用,可能用于手动控制启动流程。
---
## 工厂注册
```js
bricks.Factory.register('QAFrame', bricks.QAFrame);
```
允许通过工厂模式按名称创建实例:
```js
bricks.create({ type: 'QAFrame', ws_url: 'ws://localhost:8080' });
```
---
## 使用示例
```html
<div id="qa-container"></div>
<script>
const qa = new bricks.QAFrame({
ws_url: 'ws://example.com/ws/qa',
ws_params: {
session_id: 'abc123',
user_id: 'u456'
},
courseware: {
type: 'video',
url: '/intro.mp4',
timeout: 15
}
});
document.getElementById('qa-container').appendChild(qa.root());
</script>
```
---
## 已知问题与改进建议
| 问题 | 描述 | 建议 |
|------|------|------|
| `qtotal_w` / `qcur_w` 未定义 | `show_question` 中尝试调用 `.set_text()`,但未初始化这些 widget | 应在构造函数中创建并加入 `top_w` |
| `oncourseware` 多次绑定 | 同一事件监听器重复注册三次 | 保留一次即可 |
| `send_audio_data` 调用错误 | `bind('record_ended')` 指向不存在的方法 | 改为 `this.send_audio_answer.bind(this)` |
| `play_course()` 未调用 | 初始化课件无法自动播放 | 在合适时机调用(如构造末尾判断是否存在 `courseware` |
| `StrInput` 工厂调用不明确 | `StrInput({...})` 不符合常规语法 | 应为 `new bricks.StrInput(...)` 或使用 `widgetBuild` |
---
## 总结
`bricks.QAFrame` 是一个功能完整、结构清晰的互动问答组件,具备以下优势:
✅ 支持多种媒体格式
✅ 实时双向通信
✅ 模块化设计便于扩展
✅ 适合嵌入式教学系统
建议修复上述问题以提升稳定性和可维护性。
---
*文档版本v1.0*
*最后更新2025年4月*

View File

@ -1,410 +0,0 @@
# `bricks.MediaRecorder` 技术文档
## 概述
`bricks.MediaRecorder` 是一个基于浏览器 `MediaRecorder API` 的通用媒体录制组件,继承自 `bricks.Popup`。它提供了统一的界面和控制逻辑,支持音频、视频以及特定 DOM 元素(如 `<video>`)的录制功能。
通过继承机制,该类派生出多个具体实现:
- `WidgetRecorder`:录制指定页面中的媒体元素(如 `<video>``<audio>`
- `SysAudioRecorder`:录制系统麦克风输入(音频流),并自动转换 WebM 为 WAV 格式
- `SysVideoRecorder`:录制摄像头及麦克风输入(音视频流),实时预览画面
- `SysCamera`:仅拍照模式,用于从摄像头抓取单帧图像
所有子类均通过 `bricks.Factory` 注册,便于在应用中动态创建。
---
## 基础类:`bricks.MediaRecorder`
### 继承关系
```js
class bricks.MediaRecorder extends bricks.Popup
```
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数名 | 类型 | 必需 | 默认值 | 说明 |
|-------|------|------|--------|------|
| `opts.fps` | Number | 否 | `30` | 帧率(每秒更新次数),用于时间显示刷新频率 |
#### 初始化内容
- 设置任务周期:`this.task_period = 1 / fps`
- 创建 UI 组件:
- `preview`: 预览区域VBox
- `controls`: 控制栏HBox
- `toggle_record`: 开始/停止按钮SVG 图标)
- `timepass`: 显示已录制时间(格式:`HH:MM:SS`
- `filler`: 弹性填充空间
- `cancel`: 取消按钮(删除图标)
- 绑定事件:
- `toggle_record.click``switch_record()`
- `cancel.click``cancel_record()`
- 初始化状态:
- `record_status = 'standby'`
- 初始禁用录制按钮
- 调度 `open_recorder()` 在 0.1 秒后执行(异步准备设备)
---
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `fps` | Number | 每秒帧数,决定 UI 更新频率 |
| `task_period` | Number | 定时任务间隔(秒) |
| `task` | TaskHandle | 当前运行的时间更新定时器 |
| `pic_task` | TaskHandle | (仅视频)图像捕获定时器 |
| `stream` | MediaStream | 获取到的媒体流 |
| `mediaRecorder` | MediaRecorder | 浏览器原生录制实例 |
| `recordedChunks` | Array<Blob> | 存储录制过程中产生的数据块 |
| `mimetype` | String | 媒体 MIME 类型(如 `'audio/wav'`, `'video/mp4'` |
| `normal_stop` | Boolean | 是否正常结束录制(防止重复触发) |
| `record_status` | String | 状态:`'standby'``'recording'` |
| `start_time` | Number | 录制开始的时间戳(毫秒) |
| `time_diff` | String | 当前录制时长字符串(`HH:MM:SS` |
---
### 方法
#### `tick_task()`
定时更新录制时间显示。
```js
tick_task()
```
- 使用 `schedule_once` 循环调用自身,间隔为 `this.task_period`
- 调用 `bricks.timeDiff(this.start_time)` 计算经过时间
- 更新 `this.timepass` 文本
> ⚠️ 注意:必须手动取消任务以避免内存泄漏。
---
#### `async switch_record()`
切换录制状态(开始或停止录制)。
```js
switch_record()
```
行为根据当前 `record_status` 决定:
| 状态 | 动作 |
|------|------|
| `'standby'` | 调用 `start_recorder()`,图标变为“停止” |
| `'recording'` | 调用 `stop_recorder()`,图标恢复为“开始” |
---
#### `cancel_record()`
取消当前录制操作,关闭资源并关闭弹窗。
```js
cancel_record()
```
等价于调用 `close_recorder()`
---
#### `async open_recorder()`
**虚方法**:由子类重写以打开具体的媒体源。
```js
open_recorder()
```
> 默认实现为空,仅输出调试日志。
子类应在此方法中:
- 请求用户权限(如麦克风/摄像头)
- 获取 `MediaStream`
- 设置 `this.stream`
- 解除录制按钮禁用状态
---
#### `async start_recorder()`
启动实际录制流程。
##### 步骤:
1. 设置 `normal_stop = false`
2. 实例化 `MediaRecorder` 并绑定事件:
- `ondataavailable`: 收集数据块到 `recordedChunks`
- `onstop`: 处理最终文件生成与事件分发
3. 记录 `start_time`
4. 启动定时器更新时间显示
5. 调用 `mediaRecorder.start()`
6. 触发 `record_started` 事件
##### 事件分发
- `record_started`: 录制开始时触发
---
#### `async blob_convert(blob)`
**可扩展方法**:允许子类对录制结果 Blob 进行格式转换。
```js
blob_convert(blob) → Promise<Blob>
```
默认直接返回原始 Blob。
> 示例:`SysAudioRecorder` 中将 WebM 转换为 WAV。
---
#### `stop_recorder()`
停止录制,并标记为正常终止。
##### 行为:
- 设置 `normal_stop = true`
- 更新时间显示
- 调用 `mediaRecorder.stop()`
- 调用 `close_recorder()`
---
#### `close_recorder()`
清理资源并关闭弹窗。
##### 清理项:
- 取消 `task``pic_task`(如有)
- 停止 `mediaRecorder`(若存在)
- 停止 `stream` 所有轨道
- 调用 `dismiss()` 关闭弹窗
> 若正在录制,会强制中断。
---
## 子类详解
---
### `bricks.WidgetRecorder`
> 录制页面中某个媒体元素(如 `<video>``<audio>`
#### 继承链
```js
class WidgetRecorder extends MediaRecorder
```
#### `open_recorder()`
- 根据 `opts.widgetid` 查找目标组件
- 检查其 DOM 标签类型:
- `<video>``mimetype = 'video/mp4'`
- `<audio>``mimetype = 'audio/wav'`
- 调用 `captureStream()` 获取流
- 启用录制按钮
⚠️ 错误处理:非媒体元素抛出异常
---
### `bricks.SysAudioRecorder`
> 录制麦克风输入,支持 WebM → WAV 自动转换
#### 继承链
```js
class SysAudioRecorder extends MediaRecorder
```
#### `open_recorder()`
- 请求麦克风权限(`getUserMedia({ audio: true })`
- 设置 `mimetype = 'audio/webm'`
- 获取音频流并启用按钮
#### `blob_convert(webmBlob)`
将 WebM 编码的音频解码为 PCM再编码为标准 WAV 格式 Blob。
##### 流程:
1. `webmBlob.arrayBuffer()` → 获取二进制数据
2. 使用 `AudioContext.decodeAudioData()` 解码为 `AudioBuffer`
3. 调用 `encodeWAV()` 生成 WAV 文件头 + 数据
4. 返回新的 `Blob(type='audio/wav')`
#### `encodeWAV(audioBuffer)`
手动构建 WAV 文件二进制结构,包含 RIFF 头信息。
| 参数 | 值 |
|------|----|
| 采样率 | 来自 `audioBuffer.sampleRate` |
| 声道数 | `audioBuffer.numberOfChannels` |
| 位深 | 16-bit PCM |
| 格式 | 小端序little-endian |
使用 `DataView` 写入头部字段。
#### `interleave(left, right)`
交错左右声道数据,生成立体声 PCM 数组。
#### `writeString(view, offset, string)`
辅助函数:向 `DataView` 写入 ASCII 字符串。
---
### `bricks.SysVideoRecorder`
> 录制摄像头+麦克风,带实时预览
#### 继承链
```js
class SysVideoRecorder extends MediaRecorder
```
#### `open_recorder()`
- 请求音视频权限:`{ audio: true, video: true }`
- 设置 `mimetype = 'video/mp4'`
- 创建 `ImageCapture(track)` 对象用于逐帧捕获
- 添加 `bricks.Image` 到预览区
- 启动 `show_picture()` 循环任务
#### `show_picture()`
- 调用 `imageCapture.takePhoto()` 获取当前帧 Blob
- 转换为 Data URL 并设置给 `imgw`
- 创建 `File` 对象供后续使用
- 递归调度下一次拍摄
> 实现了类似“摄像机取景器”的效果
#### `close_recorder()`
重写以额外清理 `pic_task`
---
### `bricks.SysCamera`
> 专用拍照工具,从摄像头抓拍一张照片
#### 继承链
```js
class SysCamera extends SysVideoRecorder
```
#### `switch_record()`
重写行为为“拍照”而非录制:
##### 动作:
- 阻止事件冒泡(`event.stopPropagation()`
- 停止 `pic_task`
- 设置 `task_period = 0` 防止继续更新
- 分发 `shot` 事件,携带:
```js
{
url: this.dataurl, // 图像 Data URL
file: this.imgfile // 图像 File 对象
}
```
- 调用 `close_recorder()` 关闭
> 不进行任何视频录制,仅抓取一帧
---
## 工厂注册
以下类通过 `bricks.Factory` 注册,可在配置中通过名称实例化:
```js
bricks.Factory.register('SysCamera', bricks.SysCamera);
bricks.Factory.register('WidgetRecorder', bricks.WidgetRecorder);
bricks.Factory.register('SysAudioRecorder', bricks.SysAudioRecorder);
bricks.Factory.register('SysVideoRecorder', bricks.SysVideoRecorder);
```
示例使用方式(假设框架支持):
```json
{
"type": "SysAudioRecorder",
"opts": {
"fps": 25
}
}
```
---
## 事件列表
| 事件名 | 触发时机 | 传递数据 |
|--------|----------|---------|
| `record_started` | 录制开始时 | 无 |
| `record_end` | 录制正常结束 | `{ url: string, file: File }` |
| `shot` | SysCamera拍照完成 | `{ url: string, file: File }` |
> 使用 `this.dispatch(eventName, data)` 触发
---
## 工具函数依赖
| 函数 | 来源 | 用途 |
|------|------|------|
| `bricks_resource(path)` | 全局函数 | 解析静态资源路径 |
| `schedule_once(fn, delay)` | 全局函数 | 延迟执行函数(支持取消) |
| `bricks.timeDiff(timestamp)` | 工具模块 | 将时间戳差值转为 `HH:MM:SS` 字符串 |
| `bricks.getWidgetById(id, app)` | 框架 API | 根据 ID 获取组件实例 |
---
## 使用示例
### 启动音频录制
```js
const recorder = new bricks.SysAudioRecorder({
fps: 30
});
recorder.bind('record_end', (data) => {
console.log('录音完成:', data.file);
const audio = document.createElement('audio');
audio.src = data.url;
audio.controls = true;
document.body.appendChild(audio);
});
recorder.present(); // 显示录制界面
```
### 抓拍摄像头照片
```js
const camera = new bricks.SysCamera();
camera.bind('shot', ({ file }) => {
console.log('照片已拍摄:', file);
});
camera.present();
```
---
## 注意事项
1. **权限请求**`getUserMedia` 需要在安全上下文HTTPS中运行且用户需授权。
2. **兼容性**`MediaRecorder` 在部分旧浏览器中不支持或需前缀。
3. **内存管理**:长时间录制可能导致大量 `recordedChunks` 占用内存,建议限制最大时长。
4. **格式支持**:不同浏览器支持的 `mimeType` 不同,应做检测回退。
5. **跨域问题**`captureStream()``<video>` 要求同源或 CORS 允许。
---
## 总结
`bricks.MediaRecorder` 提供了一个模块化、可扩展的媒体录制架构,适用于多种场景:
| 类 | 用途 | 输出格式 |
|-----|------|---------|
| `WidgetRecorder` | 录屏(特定元素) | MP4 / WAV |
| `SysAudioRecorder` | 录音 | WAV经转换 |
| `SysVideoRecorder` | 录像 | MP4 |
| `SysCamera` | 拍照 | JPEG |
结合 `bricks` 框架的组件系统与事件机制,可轻松集成至可视化编辑器或交互式应用中。

View File

@ -1,183 +0,0 @@
# Bricks RegisterFunction 模块技术文档
---
## 概述
`RegisterFunction``bricks` 命名空间下的一个轻量级函数注册与管理类,用于在全局环境中注册、存储和获取函数。它提供了一种集中式的方式来管理可复用或动态调用的函数,适用于插件系统、事件处理器注册、模块化功能扩展等场景。
该模块通过 `bricks.RF` 实例暴露给全局使用,确保函数注册的统一访问接口。
---
## 代码结构
```javascript
var bricks = window.bricks || {};
class RegisterFunction {
constructor() {
this.rfs = {};
}
register(n, f) {
this.rfs[n] = f;
}
get(n) {
try {
return this.rfs[n];
} catch (e) {
return null;
}
}
}
bricks.RF = new RegisterFunction();
```
---
## 核心类:`RegisterFunction`
### 构造函数 `constructor()`
初始化一个空的对象 `this.rfs`,用于存储注册的函数。
#### 示例:
```js
const rf = new RegisterFunction(); // this.rfs = {}
```
---
### 方法:`register(n, f)`
将一个函数 `f` 注册到名称 `n` 下。
#### 参数:
- `n` (string | number): 函数的唯一标识名称(键名)。
- `f` (Function): 要注册的函数。
#### 行为:
- 将 `f` 存储在 `this.rfs` 对象中,以 `n` 作为键。
- 若已存在同名键,则覆盖原值。
#### 示例:
```js
function greet() {
console.log("Hello!");
}
bricks.RF.register('greet', greet);
```
---
### 方法:`get(n)`
根据名称 `n` 获取已注册的函数。
#### 参数:
- `n` (string | number): 要获取的函数的名称。
#### 返回值:
- 如果存在对应的函数,返回该函数;
- 如果不存在或发生异常,返回 `null`
> ⚠️ 注意:尽管 `try-catch` 被使用,但在 JavaScript 中直接访问对象属性不会抛出异常(除非对象被冻结或有 getter 抛错)。因此这里的 `try-catch` 属于防御性编程,实际多数情况下非必需。
#### 示例:
```js
const greetFn = bricks.RF.get('greet');
if (greetFn) {
greetFn(); // 输出: Hello!
}
```
---
## 全局实例:`bricks.RF`
通过以下语句创建并暴露全局唯一的 `RegisterFunction` 实例:
```js
bricks.RF = new RegisterFunction();
```
此实例可在全局范围内通过 `bricks.RF` 访问,实现跨模块的函数注册与调用。
---
## 使用示例
### 注册多个函数
```js
bricks.RF.register('log', msg => console.log(msg));
bricks.RF.register('add', (a, b) => a + b);
```
### 调用已注册函数
```js
const log = bricks.RF.get('log');
log('This is a test message.');
const add = bricks.RF.get('add');
console.log(add(2, 3)); // 输出: 5
```
### 处理未注册函数
```js
const unknown = bricks.RF.get('unknown');
console.log(unknown); // 输出: null
```
---
## 设计特点
| 特性 | 说明 |
|------|------|
| **轻量** | 仅包含两个核心方法,无外部依赖。 |
| **易用** | 接口简洁,便于集成到现有项目中。 |
| **容错处理** | `get()` 方法包含异常捕获,增强健壮性。 |
| **可扩展** | 可基于此类构建更复杂的注册中心(如带命名空间、生命周期管理等)。 |
---
## 注意事项
1. **命名冲突**:注册时使用唯一名称,避免覆盖重要函数。
2. **类型检查缺失**:当前未对传入的 `f` 是否为函数进行校验,建议调用前自行验证。
3. **性能考虑**:底层基于普通对象存储,适合中小型注册场景;高频读写可考虑 `Map` 结构优化。
---
## 改进建议(可选)
```js
// 增强版 register增加函数类型校验
register(n, f) {
if (typeof f !== 'function') {
throw new Error(`Expected a function, but got ${typeof f}`);
}
this.rfs[n] = f;
}
// 或使用 Map 提升性能(尤其在大量条目时)
this.rfs = new Map();
register(n, f) { this.rfs.set(n, f); }
get(n) { return this.rfs.get(n) || null; }
```
---
## 版本信息
- 初始版本1.0
- 作者Bricks Framework Team
- 兼容环境:现代浏览器(支持 ES6 Class
---
> 📌 提示:本模块是 `bricks` 工具库的基础组件之一,推荐与其他模块配合使用以发挥最大效能。

View File

@ -1,435 +0,0 @@
# Bricks WebRTC 通信框架技术文档
本文档描述了 `bricks` 框架中用于实现点对点P2P实时音视频通信的核心模块包括
- `bricks.VideoBox`:用于播放媒体流的视频组件。
- `bricks.Signaling`:信令服务客户端,通过 WebSocket 与服务器通信。
- `bricks.RTCP2PConnect`:基于 WebRTC 的 P2P 连接管理器。
---
## 1. `bricks.VideoBox`
一个封装了 `<video>` 元素的 JavaScript 组件,用于显示媒体流(如摄像头或屏幕共享流)。
### 类定义
```js
class bricks.VideoBox extends bricks.JsWidget
```
### 方法
#### `create()`
创建并初始化 `<video>` DOM 元素,并设置自动播放和静音属性。
**行为:**
- 创建 `video` 元素。
- 设置 `autoplay``muted` 属性为 `true`
> ⚠️ 注意:由于现代浏览器的安全策略,`autoplay` 只能在用户交互后生效,且必须配合 `muted` 才能无提示播放。
#### `get_stream() → MediaStream`
获取当前绑定到该视频元素的媒体流。
**返回值:**
- `{MediaStream}` 当前播放的媒体流对象。
#### `set_stream(stream: MediaStream)`
将指定的媒体流绑定到 `<video>` 元素上进行播放。
**参数:**
- `stream`: `{MediaStream}` 要播放的媒体流。
**行为:**
- 将 `stream` 赋值给内部变量 `this.stream`
- 设置 `this.dom_element.srcObject = stream`,触发视频播放。
---
## 2. `bricks.Signaling`
信令客户端类,负责与信令服务器建立 WebSocket 连接,处理登录、心跳、消息收发及会话分发。
### 构造函数
```js
new bricks.Signaling(opts)
```
**参数 `opts`**
| 参数 | 类型 | 描述 |
|------|------|------|
| `signaling_url` | `String` | WebSocket 信令服务器地址 |
| `info` | `Object` | 客户端信息(如用户 ID、设备信息等在发送数据时附加为 `msgfrom` 字段 |
| `connect_opts` | `Object` | 传递给后续连接模块的通用配置选项 |
| `onclose` | `Function` | WebSocket 关闭时的回调函数 |
| `onopen` | `Function` | WebSocket 成功打开时的回调函数(可选) |
| `onlogin` | `Function(onlinePeers)` | 登录成功后接收在线用户列表的回调 |
| `heartbeat_period` | `Number` | 心跳周期(秒),若不设则不启用 |
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `socket` | `WebSocket` | 当前 WebSocket 实例 |
| `peers` | `Object` | 存储当前在线用户的映射表key: peer.id |
| `sessions` | `Object` | 已建立的会话记录(未使用) |
| `handlers` | `Object` | 每个会话 ID 对应的消息处理器 |
| `sessionhandlers` | `Object` | 按会话类型注册的处理器构造函数 |
| `hb_task` | `TaskHandle` | 心跳定时任务句柄(由 `schedule_once` 返回) |
| `heartbeat_period` | `Number` | 心跳间隔时间(秒) |
### 方法
#### `init_websocket()`
初始化 WebSocket 连接:
- 使用 `bricks.app.get_session()` 获取会话凭证作为子协议。
- 绑定事件监听器:
- `onmessage``signaling_recvdata`
- `onopen``login`
- `onclose` / `onerror``reconnect`
#### `reconnect()`
WebSocket 断开后的处理逻辑:
- 清除心跳任务。
- 触发 `onclose` 回调。
- **注意:当前未实现自动重连机制。**
#### `signaling_recvdata(event)`
异步处理从 WebSocket 接收到的数据包。
**流程:**
1. 解析 JSON 数据。
2. 若包含 `data.session`,查找对应会话处理器:
- 若首次收到,则根据 `sessiontype` 实例化对应的处理器类。
- 注册该会话的 `recvdata_handler`
3. 否则调用默认 `recvdata_handler` 处理全局消息。
#### `add_handler(key, handler)`
注册某个 `sessionid` 的消息处理器。
#### `add_sessionhandler(sessiontype, handlerClass)`
注册某种会话类型的处理器类(构造函数)。例如:
```js
signaling.add_sessionhandler('p2p', bricks.RTCP2PConnect);
```
#### `recvdata_handler(data)`
默认消息处理器,处理非会话级消息。
**支持的消息类型:**
- `online`: 更新在线用户列表,并触发 `onlogin(online)` 回调。
> ❗ 未处理其他消息类型时仅打印日志。
#### `new_session(sessiontype, peer)`
发起一个新的会话请求。
**步骤:**
1. 查找 `sessiontype` 对应的处理器类。
2. 生成唯一 `sessionid`
3. 发送 `{ type: 'new_session', session }` 到服务器。
4. 创建处理器实例并注册其 `recvdata_handler`
5. 返回处理器实例(通常是 `RTCP2PConnect` 实例)。
#### `login()`
向服务器发送登录请求。
**行为:**
- 发送 `{ type: 'login' }`
- 如果设置了 `heartbeat_period > 0`,启动周期性心跳任务。
#### `logout()`
发送登出请求 `{ type: 'logout' }`
#### `send_data(d)`
将对象序列化为 JSON 并通过 WebSocket 发送。
**附加字段:**
- `d.msgfrom = this.info`(标识发送者)
#### `socket_send(s)`
直接发送原始字符串(底层接口,一般不推荐直接使用)。
---
## 3. `bricks.RTCP2PConnect`
WebRTC P2P 连接控制器,处理音视频通话和数据通道通信。
### 构造函数
```js
new bricks.RTCP2PConnect(signaling, session, opts)
```
**参数:**
| 参数 | 类型 | 描述 |
|------|------|------|
| `signaling` | `bricks.Signaling` | 信令客户端实例 |
| `session` | `Object` | 会话元信息 `{ sessionid, sessiontype }` |
| `opts` | `Object` | 配置项,见下表 |
**`opts` 配置说明:**
| 选项 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `ice_servers` | `Array` | 必需 | ICE 服务器配置STUN/TURN |
| `peer_info` | `Object` | 必需 | 对端用户信息(含 `.id` |
| `auto_callaccept` | `Boolean` | `false` | 是否自动接受呼叫 |
| `media_options` | `Object` | `{ video: true, audio: true }` | 媒体采集选项 |
| `data_connect` | `Boolean` | `false` | 是否启用数据通道 |
| `on_pc_connected(peer)` | `Function` | - | 连接建立成功回调 |
| `on_pc_disconnected(peer)` | `Function` | - | 连接断开回调 |
| `on_dc_open(dc)` | `Function` | - | 数据通道开启回调 |
| `on_dc_close(dc)` | `Function` | - | 数据通道关闭回调 |
| `on_dc_message(dc, data)` | `Function` | - | 收到数据通道消息回调 |
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `id` | `String` | 本地唯一标识UUID |
| `signaling` | `Signaling` | 信令客户端引用 |
| `session` | `Object` | 当前会话信息 |
| `requester` | `Boolean` | 是否为主叫方 |
| `peers` | `Object` | 所有远程连接的 `PeerConnection` 管理对象 |
| `signal_handlers` | `Object` | 信令消息处理器映射 |
| `local_stream` | `MediaStream` | 本地摄像头流 |
| `localVideo` | `VideoBox` | 本地视频预览组件 |
| `local_screen` | `MediaStream` | 屏幕共享流(可选) |
---
### 核心方法
#### `add_handler(type, f)`
注册信令消息处理器(如 `'offer'`, `'answer'` 等)。
#### `get_handler(type) → Function`
获取指定类型的消息处理器。
#### `p2pconnect(peer)`
尝试与指定对端建立 P2P 连接。
**流程:**
1. 获取本地媒体流(如果尚未获取)。
2. 若已存在连接则跳过。
3. 调用 `createPeerConnection(peer)` 创建新的 `RTCPeerConnection`
#### `h_sessioncreated(data)`
当会话创建完成时触发。
**行为:**
- 自动调用 `p2pconnect`
- 若有 `peer_info`,主动发送 `callrequest` 请求呼叫。
#### `h_callrequest(data)`
收到呼叫请求。
**行为:**
- 若 `auto_callaccept === true`(或硬编码为 `true`),自动响应:
- 建立连接。
- 发送 `callaccepted` 消息。
#### `h_callaccepted(data)`
对方接受呼叫。
**行为:**
- 创建数据通道(如果启用)。
- 发送 SDP Offer。
#### `h_offer(data)`
收到远程 Offer。
**流程:**
1. 设置 `remoteDescription`
2. 创建 Answer。
3. 设置 `localDescription`
4. 发送 Answer 回去。
5. 如果不是主叫方,再发送一次 Offer可能存在设计问题需确认逻辑
#### `h_answer(data)`
收到远程 Answer。
**行为:**
- 设置 `remoteDescription`
#### `h_icecandidate(data)`
收到 ICE 候选地址。
**行为:**
- 添加到 `RTCPeerConnection` 中。
#### `h_sessionquit(data)`
收到会话结束通知。
**行为:**
- 关闭对应 `RTCPeerConnection`
#### `send_offer(peer, initial = false)`
创建并发送 SDP Offer。
**参数:**
- `peer`: 目标用户。
- `initial`: 是否是首次呼叫(决定角色:`requester` / `responser`
**流程:**
1. 创建 Offer。
2. 设置 `localDescription`
3. 通过信令发送 Offer。
#### `send_candidate(peer, event)`
ICE 候选事件触发时调用,转发候选地址给远端。
> ✅ 使用 `bind(this, peer)` 绑定上下文。
#### `signaling_send(d)`
封装信令消息,附带当前 `session` 信息后发送。
#### `recvdata_handler(data)`
统一入口处理所有信令消息:
- 查找对应处理器并执行。
- 未处理则输出警告日志。
---
### 连接状态监听
#### `ice_statechange(peer, event)`
ICE 连接状态变化时的日志输出。
#### `connection_statechange(peer, event)`
连接状态变化处理:
- `disconnected`: 触发 `peer_close()``on_pc_disconnected` 回调。
- `connected`: 触发 `on_pc_connected` 回调。
---
### 数据通道DataChannel
#### `dc_accepted(peer, event)`
收到远程数据通道请求。
**行为:**
- 保存 `event.channel`
- 调用 `dc_created()` 初始化。
#### `dc_created(peer, dc)`
初始化数据通道事件监听器:
- `onmessage``datachannel_message`
- `onopen``datachannel_open`
- `onclose``datachannel_close`
#### `datachannel_message(peer, event)`
收到数据通道消息。
**行为:**
- 触发 `on_dc_message(dc, event.data)` 回调。
#### `datachannel_open/close(peer)`
分别在数据通道打开/关闭时触发相应回调。
#### `createDataChannel(peer)`
主动创建数据通道(用于主叫方)。
---
### 媒体流控制
#### `createPeerConnection(peer)`
创建并配置 `RTCPeerConnection` 实例。
**关键操作:**
- 使用传入的 `iceServers`
- 添加本地媒体轨道(如有)。
- 绑定以下事件:
- `onicecandidate`
- `oniceconnectionstatechange`
- `onconnectionstatechange`
- `ondatachannel`(注意:拼写错误应为 `ondatachannel`
- `ontrack` → 将远端流绑定到 `VideoBox`
> 🛠️ Bug 提示:`pc.ondatachanel` 应为 `pc.ondatachannel`
#### `changeLocalVideoStream(peer, new_stream)`
动态更换本地视频流(如切换摄像头或屏幕共享)。
**流程:**
1. 移除旧的视频 Track。
2. 添加新流中的视频 Track。
3. 重新发送 Offer 以更新协商。
#### `getLocalStream()`
获取本地摄像头和屏幕共享流。
**行为:**
- 调用 `getUserMedia(media_options)` 获取摄像头流。
- 调用 `getDisplayMedia({ video: true })` 获取屏幕共享流(音频可选)。
- 分别赋值给 `this.local_stream``this.local_screen`
- 创建 `localVideo` 并绑定摄像头流用于预览。
> 🔐 权限提示:需要用户授权麦克风和摄像头权限。
---
#### `peer_close(peer)`
清理与指定对端的所有资源。
**释放内容:**
- 停止所有接收/发送的媒体 Track。
- 停止远端视频流。
- 关闭数据通道和 `RTCPeerConnection`
- 删除 `peers` 中的记录。
- 若无剩余连接,停止本地流并从信令中注销会话处理器。
---
## 使用示例
```js
// 初始化信令
var signaling = new bricks.Signaling({
signaling_url: 'wss://example.com/signaling',
info: { id: 'user123', name: 'Alice' },
heartbeat_period: 30,
onlogin: function(peers) {
console.log('Online users:', peers);
}
});
// 注册 P2P 处理器
signaling.add_sessionhandler('p2p', bricks.RTCP2PConnect);
// 登录(触发 login
signaling.login();
// 发起一个 P2P 会话
var p2p = signaling.new_session('p2p', { id: 'user456' });
```
---
## 注意事项与潜在问题
| 问题 | 描述 | 建议修复 |
|------|------|---------|
| `ondatachanel` 拼写错误 | 应为 `ondatachannel` | 更正拼写 |
| `auto_callaccept || true` | 条件恒真,失去意义 | 改为 `this.opts.auto_callaccept === true` |
| `newStream` 变量名错误 | `changeLocalVideoStream` 中误写为 `newStream` | 改为 `new_stream` |
| 缺少异常处理 | 多处 `await` 未包裹 `try/catch` | 增加错误捕获 |
| 心跳任务未清除 | `schedule_once` 可能导致内存泄漏 | 确保正确取消任务 |
| 未实现自动重连 | `reconnect()` 仅触发回调 | 应加入延迟重试机制 |
---
## 总结
`bricks` 提供了一套完整的 WebRTC 信令与连接管理方案,具有以下特点:
**模块化设计**:分离信令、会话、连接逻辑
**扩展性强**:支持多种会话类型
**易于集成**:提供 VideoBox 等 UI 组件封装
⚠️ **需优化点**:存在若干 bug 与安全限制需处理
适用于开发音视频会议、直播互动、远程协作等实时通信应用。

View File

@ -1,198 +0,0 @@
# `bricks.Running` 模块技术文档
---
## 概述
`bricks.Running` 是一个用于显示运行中状态(如加载、处理中)的模态窗口组件,通常用于提示用户当前系统正在执行某项任务。它基于 `bricks.BaseModal` 和自定义的 `bricks.BaseRunning` 类构建,提供了一个动态更新的时间计时器和一个可配置的动画图标(默认为 `running.gif`)。
该模块适用于需要可视化反馈长时间操作进度的场景。
---
## 依赖说明
- `bricks.FHBox`:水平布局容器基类。
- `bricks.BaseModal`:模态窗口基类。
- `bricks.Icon`:图标显示组件。
- `bricks.Text`:文本显示组件。
- `bricks.formatMs()`:格式化毫秒时间的工具函数。
- `schedule_once()`:定时调度函数,用于周期性执行任务。
- `bricks_resource()`:资源路径解析函数,用于获取静态资源 URL。
> ⚠️ 注意:以上依赖需在运行环境中提前定义并可用。
---
## 类结构
### 1. `bricks.BaseRunning`
继承自 `bricks.FHBox`,用于构建包含图标与运行时间显示的横向控件。
#### 构造函数:`constructor(opts)`
##### 参数
| 参数名 | 类型 | 是否必填 | 描述 |
|--------|--------|----------|------|
| `opts.icon` | String | 否 | 图标图片的 URL 路径。若未提供,则使用默认资源路径下的 `running.gif`。 |
##### 功能说明
1. 调用父类构造函数。
2. 创建一个 `Icon` 组件用于展示运行动画。
3. 创建一个 `Text` 组件用于实时显示已运行时间。
4. 初始化起始时间戳(`this.time_start`)。
5. 将图标和文本控件添加到容器中。
6. 启动定时任务,每 50ms 更新一次时间显示。
##### 示例代码
```js
const runner = new bricks.BaseRunning({
icon: '/custom/loading.svg'
});
```
#### 方法:`show_timepass()`
- **功能**:计算从开始到当前经过的时间,并格式化后更新到 `Text` 控件上。
- **触发机制**:通过 `schedule_once` 每 50ms 自调用一次,形成持续更新效果。
- **时间格式**:调用 `bricks.formatMs(t, 1)`,保留一位小数,单位为秒(例如:`3.2s`)。
#### 方法:`stop_timepass()`
- **功能**:停止时间更新任务。
- **行为**
- 如果存在正在进行的定时任务(`this.showtime_task`),则取消该任务。
- 清空任务引用,防止内存泄漏。
---
### 2. `bricks.Running`
继承自 `bricks.BaseModal`,封装 `BaseRunning` 成一个自动弹出的模态框。
#### 构造函数:`constructor(opts)`
##### 参数
| 参数名 | 类型 | 是否必填 | 描述 |
|------------|--------|----------|------|
| `opts.target` | Element | 否 | 模态框挂载的目标 DOM 元素。默认为 body 或由 BaseModal 决定。 |
| `opts.icon` | String | 否 | 使用的图标路径,将传递给 `BaseRunning`。 |
##### 特性设置
- `auto_open: true`:实例化后自动打开模态框。
- `archor: 'cc'`模态框居中对齐center-center
##### 内部逻辑
- 创建 `bricks.BaseRunning` 实例作为内容主体。
- 将其加入模态框内容区域。
#### 方法:`dismiss()`
- **功能**:关闭模态框前,先停止计时器。
- **步骤**
1. 调用 `this.w.stop_timepass()` 停止时间更新。
2. 调用父类 `BaseModal.dismiss()` 关闭模态框。
> ✅ 提示:此方法确保资源释放和定时器清理。
---
### 3. 工厂注册
```js
bricks.Factory.register('Running', bricks.Running);
```
- 将 `Running` 组件注册到 `bricks.Factory` 中,支持通过字符串名称动态创建实例:
```js
bricks.Factory.create('Running', { icon: 'imgs/custom.gif' });
```
---
## 使用示例
### 示例 1基本使用默认图标
```js
new bricks.Running();
// 弹出居中模态框,显示默认 running.gif 与运行时间
```
### 示例 2自定义图标
```js
new bricks.Running({
icon: '/assets/icons/spinner.svg'
});
```
### 示例 3手动关闭
```js
const runner = new bricks.Running();
// 在某个异步操作完成后关闭
setTimeout(() => {
runner.dismiss(); // 自动停止计时器并关闭模态框
}, 3000);
```
---
## 样式与外观
| 组件 | 属性 | 值 |
|------------|-------------|------------------------|
| 文本颜色 | color | `#222` |
| 文本换行 | wrap | `false` |
| 国际化支持 | i18n | `false` |
| 容器布局 | FHBox | 水平排列图标与时间文本 |
| 模态框锚点 | archor | `'cc'`(居中) |
> 可通过扩展 CSS 或子类进一步定制样式。
---
## 性能与注意事项
- **刷新频率**:每 50ms 更新一次时间,平衡流畅性与性能。
- **资源管理**
- 必须调用 `dismiss()` 正确关闭,避免定时器持续运行。
- 不手动调用可能导致内存泄漏或界面卡顿。
- **跨浏览器兼容性**:依赖 `Date.now()``bind()`,建议配合 polyfill 在旧浏览器中使用。
---
## API 总结
| 类名 | 方法 / 属性 | 类型 | 描述 |
|-------------------|------------------------|----------|------|
| `bricks.BaseRunning` | `constructor(opts)` | 构造函数 | 初始化带图标的运行状态条 |
| | `show_timepass()` | 方法 | 定时更新运行时间 |
| | `stop_timepass()` | 方法 | 停止时间更新任务 |
| `bricks.Running` | `constructor(opts)` | 构造函数 | 创建并自动打开模态框 |
| | `dismiss()` | 方法 | 停止计时并关闭模态框 |
| `bricks.Factory` | `.register(type, cls)` | 静态方法 | 注册组件以便工厂创建 |
---
## 版本信息
- **作者**Bricks Framework Team
- **版本**1.0
- **最后更新**2025年4月5日
> 更多文档请参考 [Bricks UI 官方文档](https://bricks.example.com/docs)
---
📌 **建议用途**:数据加载、文件上传、后台处理等耗时操作的状态提示。

View File

@ -1,293 +0,0 @@
# Bricks.js 滚动面板组件技术文档
> **项目名称**`bricks.js`
> **模块**`HScrollPanel``VScrollPanel`
> **功能描述**:提供可监听滚动位置阈值的水平和垂直滚动容器组件。
---
## 目录
- [概述](#概述)
- [依赖说明](#依赖说明)
- [核心类说明](#核心类说明)
- [`low_handle`](#low_handle)
- [`bricks.HScrollPanel`](#bricksHScrollPanel)
- [`bricks.VScrollPanel`](#bricksVScrollPanel)
- [事件机制](#事件机制)
- [工厂注册](#工厂注册)
- [使用示例](#使用示例)
- [注意事项](#注意事项)
---
## 概述
`bricks.HScrollPanel``bricks.VScrollPanel` 是基于 `bricks.HBox``bricks.VBox` 的扩展类,用于创建具有滚动能力的容器,并在用户滚动接近内容边界时触发自定义事件(如 `min_threshold``max_threshold`),适用于实现“无限滚动”、“触底加载”等功能。
该组件通过封装原生 DOM 滚动行为,在关键滚动位置触发语义化事件,便于上层逻辑响应。
---
## 依赖说明
本模块依赖以下环境或模块:
- 全局对象 `window.bricks`:框架主命名空间。
- `bricks.debug()`:调试日志输出函数(仅用于开发期)。
- `bricks.HBox` / `bricks.VBox`:基础布局容器类。
- `bricks.Widget.dispatch()`:事件派发方法。
- `bricks.Factory.register()`:组件工厂注册接口。
---
## 核心类说明
### `low_handle`
#### 功能
通用滚动阈值判断函数,根据当前滚动位置判断是否到达最小或最大阈值,并触发相应事件。
#### 函数签名
```js
function low_handle(widget, dim, last_pos, cur_pos, maxlen, winsize)
```
#### 参数说明
| 参数名 | 类型 | 描述 |
|-------------|----------|------|
| `widget` | Object | 触发事件的组件实例(需支持 `.dispatch()` 方法) |
| `dim` | String | 滚动方向标识(`'x'``'y'`,目前未实际使用但预留) |
| `last_pos` | Number | 上一次记录的滚动位置 |
| `cur_pos` | Number | 当前滚动位置(例如 `scrollLeft``scrollTop` |
| `maxlen` | Number | 整体滚动内容长度(`scrollWidth``scrollHeight` |
| `winsize` | Number | 可见区域大小(`clientWidth``clientHeight` |
#### 行为逻辑
1. 如果当前滚动位置接近右/下边缘差值小于5像素则触发 `max_threshold` 事件。
2. 如果当前滚动位置接近左/上边缘小于1像素则触发 `min_threshold` 事件。
3. 使用 `bricks.debug()` 输出调试信息。
#### 示例调用
```js
low_handle(this, 'x', this.last_scrollLeft, e.scrollLeft, e.scrollWidth, e.clientWidth);
```
---
### bricks.HScrollPanel
水平滚动面板组件,继承自 `bricks.HBox`
#### 继承关系
```js
class HScrollPanel extends bricks.HBox
```
#### 构造函数
```js
constructor(opts)
```
##### 参数
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `opts.width` | String | 否 | `'100%'` | 宽度设置 |
| `opts.height` | String | 否 | `'100%'` | 高度设置 |
| `opts.css` | String | 否 | `''` | 自定义 CSS 类名,自动附加 `scrollpanel` |
| `opts.overflow` | String | 否 | `'auto'` | 溢出处理方式 |
| `opts.min_threshold` | Number | 否 | `0.01` | 最小滚动比例阈值(暂未使用) |
| `opts.max_threshold` | Number | 否 | `0.99` | 最大滚动比例阈值(暂未使用) |
> ⚠️ 注:虽然传入了 `min_threshold``max_threshold`,但在当前版本中并未参与计算,而是直接使用像素差值判断。
##### 初始化行为
- 设置默认宽高为 100%。
- 添加 `scrollpanel` CSS 类。
- 设置 `overflow: auto` 以启用滚动条。
- 注册 `scroll` 事件监听器。
- 记录初始 `scrollLeft` 值。
#### 方法
##### `scroll_handle(event)`
处理滚动事件的核心方法。
###### 参数
- `event`: DOM 滚动事件对象。
###### 逻辑流程
1. 确保事件源是当前组件的 DOM 元素。
2. 检查是否有实际可滚动空间(`scrollWidth > clientWidth + 1`)。
3. 调用 `low_handle` 判断是否触发边界事件。
4. 更新 `scroll_info` 状态对象及 `last_scrollLeft`
###### `scroll_info` 结构
```js
{
left: number, // 当前 scrollLeft
scroll_width: number, // scrollWidth
client_width: number // clientWidth
}
```
---
### bricks.VScrollPanel
垂直滚动面板组件,继承自 `bricks.VBox`
#### 继承关系
```js
class VScrollPanel extends bricks.VBox
```
#### 构造函数
```js
constructor(opts)
```
##### 参数
`HScrollPanel` 类似,区别如下:
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `min_threshold` | `0.02` | 垂直方向最小阈值 |
| `max_threshold` | `0.95` | 垂直方向最大阈值 |
> ⚠️ 当前版本中这些阈值仍未被算法使用。
##### 初始化行为
- 设置默认宽高。
- 添加 `scrollpanel` 类。
- 开启 `overflow: auto`
- 绑定 `scroll` 事件。
- 记录初始 `scrollTop`
#### 方法
##### `scroll_handle(event)`
处理垂直滚动事件。
###### 逻辑流程
1. 验证事件目标。
2. 检查是否存在可滚动高度。
3. 调用 `low_handle` 判断上下边界。
4. 更新 `scroll_info``last_scrollTop`
###### `scroll_info` 结构
```js
{
top: number, // scrollTop
scroll_height: number, // scrollHeight
client_height: number // clientHeight
}
```
---
## 事件机制
组件会在特定滚动条件下自动派发事件:
| 事件名 | 触发条件 |
|------------------|---------|
| `'min_threshold'` | 滚动位置接近顶部或左侧(`< 1px` |
| `'max_threshold'` | 滚动位置接近底部或右侧(距离末端 < 5px |
可通过 `.bind()` 方法监听这些事件:
```js
panel.bind('min_threshold', function(){
console.log('到达顶部/起点');
});
```
---
## 工厂注册
为了支持动态创建,两个组件均注册到 `bricks.Factory`
```js
bricks.Factory.register('VScrollPanel', bricks.VScrollPanel);
bricks.Factory.register('HScrollPanel', bricks.HScrollPanel);
```
这意味着可以通过工厂方式实例化:
```js
var vpanel = bricks.Factory.create('VScrollPanel', { ... });
```
---
## 使用示例
### 创建一个垂直滚动面板并监听边界事件
```js
var panel = new bricks.VScrollPanel({
css: 'my-scroll-container',
min_threshold: 0.02,
max_threshold: 0.95
});
panel.bind('min_threshold', function() {
console.log('已滚动到顶部,可加载更多...');
});
panel.bind('max_threshold', function() {
console.log('已滚动到底部,加载新数据...');
});
// 将 panel 添加到父容器中
parent.append(panel);
```
### 创建水平滚动面板
```js
var hpanel = new bricks.HScrollPanel({
css: 'horizontal-list'
});
hpanel.bind('max_threshold', function() {
alert('已滑动到最右侧!');
});
```
---
## 注意事项
1. **阈值未生效问题**:尽管构造函数接受 `min_threshold``max_threshold`,但当前 `low_handle` 使用的是固定像素判断(`<1``<5`),未来建议改为基于比例的动态判断以提升灵活性。
2. **性能优化建议**
- 可添加防抖debounce机制避免频繁触发。
- 在复杂场景下考虑使用 `requestAnimationFrame` 控制检测频率。
3. **兼容性要求**
- 支持现代浏览器。
- 依赖标准 DOM 属性如 `scrollLeft`, `scrollTop`, `scrollWidth`, `clientHeight` 等。
4. **调试模式**
- 使用 `bricks.debug()` 输出日志,上线前应关闭或重定向。
5. **CSS 要求**
- 推荐为 `.scrollpanel` 设置明确的尺寸和 `overflow` 样式,确保滚动正常工作。
---
## 版本信息
- 编写时间2025年4月
- 模块版本v1.0(基础功能版)
- 维护建议:增加对比例阈值的支持,提升可配置性。
---
✅ **文档完**

View File

@ -1,4 +0,0 @@
# bricks对服务器的要求
与bricks框架同时开发的有一个[gadget后台服务器](https://git.kaiyuancloud.cn/yumoqing/gadget),但只要符合一下要求,后台服务器也可以是其他任何服务器

View File

@ -1,139 +0,0 @@
# `bricks.Splitter` 组件技术文档
## 概述
`bricks.Splitter` 是一个基于 `bricks.JsWidget` 的轻量级 UI 组件,用于在页面中创建分隔线(`<hr>` 元素)。它通常用于视觉上分隔不同内容区域,适用于构建模块化布局。
该组件通过 Bricks 框架的工厂模式注册,可通过 `bricks.Factory` 实例化并插入 DOM。
---
## 类定义
```javascript
bricks.Splitter = class extends bricks.JsWidget
```
- **继承自**: `bricks.JsWidget`
- **用途**: 创建水平分隔线(`<hr>`
- **注册名称**: `'Splitter'`
---
## 构造函数
### `constructor(ops)`
初始化 `Splitter` 实例。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `ops` | Object | 可选配置对象(当前未使用,传递给父类) |
#### 实现细节
```js
constructor(ops) {
super({});
}
```
- 调用父类 `bricks.JsWidget` 的构造函数。
- 当前不依赖任何配置项,传入空对象 `{}`
---
## 方法
### `create()`
创建并初始化组件的 DOM 结构。
#### 实现
```js
create() {
this.dom_element = this._create('hr');
}
```
#### 行为说明
- 使用父类提供的 `_create(tagName)` 工具方法创建一个 `<hr>` 元素。
- 将生成的 DOM 元素赋值给实例属性 `this.dom_element`,供后续操作使用。
- `<hr>` 是标准 HTML 水平分隔线元素,语义清晰,样式可通过 CSS 自定义。
---
## 注册与使用
### 注册组件
```js
bricks.Factory.register('Splitter', bricks.Splitter);
```
- 将 `Splitter` 类注册到 `bricks.Factory` 中,注册名为 `'Splitter'`
- 注册后可通过工厂方法动态创建实例。
---
## 使用示例
### 1. 手动实例化
```js
const splitter = new bricks.Splitter();
splitter.create();
document.body.appendChild(splitter.getElement()); // 假设 getElement() 返回 dom_element
```
### 2. 通过工厂创建(推荐)
```js
const splitter = bricks.Factory.create('Splitter');
splitter.create();
container.appendChild(splitter.dom_element);
```
---
## DOM 结构
生成的 DOM 元素:
```html
<hr>
```
> 样式可由外部 CSS 控制,例如:
>
> ```css
> hr {
> border: 1px solid #ccc;
> margin: 16px 0;
> }
> ```
---
## 注意事项
- `create()` 方法必须被显式调用以生成 DOM 元素。
- `dom_element` 属性在 `create()` 调用后才可用。
- 当前版本不支持自定义标签或复杂配置,如需扩展可继承此类或添加参数支持。
---
## 版本兼容性
- 依赖 `bricks.JsWidget` 基类和 `bricks.Factory` 系统。
- 需确保 `window.bricks` 已初始化。
---
## 总结
`bricks.Splitter` 是一个简洁、语义化的分隔线组件,适合在组件化界面中快速插入视觉分隔。其设计遵循 Bricks 框架规范,易于集成与扩展。

View File

@ -1,240 +0,0 @@
# `bricks.StreamAudio` 技术文档
> **模块**`bricks.StreamAudio`
> **继承自**`bricks.VBox`
> **用途**实现基于麦克风音频流的实时语音识别ASR前端组件支持启动/停止录音、音频上传与实时文本反馈。
---
## 概述
`bricks.StreamAudio` 是一个用于浏览器端实时语音处理的 UI 组件。它封装了麦克风采集、语音活动检测VAD、音频编码上传以及后端流式响应接收的完整流程适用于在线语音识别ASR场景。
该组件通过按钮控制录音启停,使用 `vad.MicVAD` 进行语音端点检测,并将语音片段以 Base64 编码形式发送到指定服务端 URL同时接收并显示服务端返回的识别结果文本。
---
## 依赖说明
- `bricks.VBox`:布局容器,垂直排列子控件。
- `bricks.Button`:用于触发开始/停止操作。
- `bricks.Filler`:可伸缩填充容器,容纳内容区域。
- `bricks.Text`:显示识别出的文字内容,支持自动换行。
- `vad.MicVAD`:语音活动检测模块(假设为外部导入的 VAD 库)。
- `schedule_once`:延迟执行函数(通常为框架提供的异步调度工具)。
- `btoa`JavaScript 内置函数,用于二进制数据转 Base64 字符串。
- `TextDecoder` / `ReadableStream`:用于解析流式响应体中的 UTF-8 文本。
---
## 构造函数
```js
constructor(opts)
```
### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts` | Object | 是 | 配置选项对象 |
#### 支持的配置项:
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `height` | String | `'100%'` | 容器高度(会被强制设置) |
| `name` | String | `'asr_text'` | 组件名称标识 |
| `url` | String | — | 流式上传的目标服务器地址(必须传入) |
> ⚠️ 注意:`opts.url` 必须在初始化时提供,否则上传无法进行。
### 初始化行为
1. 设置默认高度和名称;
2. 调用父类构造函数(创建 VBox 布局);
3. 创建内部控件:
- `this.button`:控制录音启停;
- `this.filler`:中间填充区;
- `this.text_w`:显示识别结果的文本控件;
4. 将 `text_w` 添加至 `filler` 中;
5. 将 `button``filler` 添加为子组件;
6. 绑定按钮点击事件到 `toggle_status` 方法。
---
## 核心方法
### `toggle_status()`
切换录音状态(开始 ↔ 停止)
```js
toggle_status()
```
根据当前是否正在上传 (`this.upstreaming`) 决定调用 `start()``stop()`
---
### `start()`
请求开始录音与上传流程。
```js
start()
```
#### 行为:
- 更新按钮文字为 "stop"
- 使用 `schedule_once` 延迟调用 `_start()`,避免同步阻塞 UI。
---
### `async _start()`
实际执行录音启动逻辑的异步方法。
#### 步骤:
1. 若存在全局 VAD 实例(`bricks.vad`),先停止它(防止冲突);
2. 初始化新的 `MicVAD` 实例:
- 配置 `onSpeechEnd` 回调:当检测到语音结束时,调用 `handle_audio(audio)` 发送音频;
3. 启动 VAD 监听;
4. 记录当前实例为全局唯一活跃 VAD`bricks.vad = this`
5. 初始化 `UpStreaming` 实例用于上传音频;
6. 发起连接请求(`.go()`
7. 开始接收服务端响应数据(`recieve_data()`)。
> ✅ 支持并发保护:确保同一时间只有一个录音流激活。
---
### `stop()`
请求停止录音与上传。
```js
stop()
```
#### 行为:
- 更新按钮文字为 "start"
- 延迟调用 `_stop()`
---
### `async _stop()`
实际停止逻辑。
#### 步骤:
1. 如果有活跃的上传连接(`upstreaming`),调用其 `finish()` 方法通知服务端结束;
2. 清理 `upstreaming` 引用;
3. 暂停 VAD 录音;
4. 解除全局引用(`bricks.vad = null`)。
---
### `async receive_data()`
从服务端流式响应中读取并处理识别结果。
```js
async receive_data()
```
#### 工作流程:
1. 获取响应体的 `reader`(假设 `resp` 已定义且为 `Response.body.getReader()`
2. 使用 `TextDecoder` 解码 UTF-8 数据;
3. 循环读取每一行文本(模拟逐行解析 SSE 或 NDJSON 流);
4. 尝试将每行解析为 JSON
- 成功 → 提取 `d.content` 并更新显示文本;
- 失败 → 输出错误日志;
5. 直到流结束。
> ❗⚠️ 当前代码中 `resp` 变量未定义或作用域错误,应为 `this.resp` 或来自 `this.upstreaming.go()` 的返回值。**此为潜在 bug**。
---
### `handle_audio(audio)`
处理捕获到的音频数据块(通常是 PCM 或 WAV 格式)。
```js
handle_audio(audio)
```
#### 功能:
- 打印调试信息;
- 将原始音频数据转换为 Base64 字符串;
- 调用 `this.upstreaming.send(b64audio)` 发送到服务端。
> 🔍 说明:音频格式需与服务端兼容(如小端 PCM、16kHz 单声道等)。
---
## 注册与别名
```js
bricks.Factory.register('StreamAudio', bricks.StreamAudio);
bricks.Factory.register('ASRText', bricks.StreamAudio);
```
允许通过两种标签创建该组件:
- `<widget type="StreamAudio">`
- `<widget type="ASRText">`
便于不同项目或历史兼容使用。
---
## 示例用法
```html
<widget type="ASRText" url="/api/asr/stream"></widget>
```
或通过 JS 创建:
```js
const asrWidget = new bricks.StreamAudio({
url: '/api/asr/upload',
name: 'my_asr'
});
document.body.appendChild(asrWidget.dom);
```
---
## 状态属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `upstreaming` | UpStreaming \| null | 当前上传连接实例 |
| `vad` | MicVAD \| null | 语音活动检测实例 |
| `resp_text` | String | 累积的响应文本(暂未使用) |
| `resp` | Response | 服务端响应对象(可能需要修正作用域) |
---
## 已知问题与改进建议
| 问题 | 描述 | 建议修复 |
|------|------|----------|
| `resp` 未定义 | `receive_data()` 中直接使用 `resp.body`,但无声明 | 应从 `this.upstreaming.go()` 返回值获取响应对象 |
| `reader.readline()` 不存在 | 原生 `ReadableStreamDefaultReader` 不提供 `.readline()` | 需自行实现逐行读取逻辑或引入辅助库 |
| 错误拼写 | `recieve_data` 函数名拼写错误(应为 `receive` | 重命名函数及所有调用处 |
| 异常处理不足 | 接收流过程中缺少中断和重连机制 | 添加网络错误监听与重试逻辑 |
---
## 总结
`bricks.StreamAudio` 是一个功能完整的语音输入前端组件,适合集成于语音助手、实时字幕、语音搜索等系统中。虽然目前存在少量代码瑕疵,但整体结构清晰、职责分明,易于扩展和维护。
建议结合后端 ASR 服务(如 WebSocket + Whisper 或自研引擎)共同部署使用。
---
📌 **版本**1.0
📅 **最后更新**2025-04-05

View File

@ -1,350 +0,0 @@
# `bricks.Svg` 与相关组件技术文档
本文件为 `bricks.Svg` 及其派生类(`StatedSvg``MultipleStateIcon`)的详细技术说明文档。这些类用于在网页中动态加载、渲染和控制 SVG 图标的行为支持颜色设置、闪烁动画、URL 动态更新以及状态切换功能。
---
## 模块结构
```js
var bricks = window.bricks || {};
```
所有组件均挂载于全局命名空间 `bricks` 下,并通过 `bricks.Factory.register()` 注册为可实例化的 UI 组件。
---
## 1. `bricks.Svg`
### 简介
`bricks.Svg` 是一个继承自 `bricks.VBox` 的类,用于显示并控制单个 SVG 图标的可视化行为。它支持从远程 URL 加载 SVG 内容,动态着色、闪烁动画等特性。
### 继承关系
```js
class Svg extends bricks.VBox
```
---
### 构造函数:`constructor(opts)`
#### 参数
| 参数 | 类型 | 必需 | 默认值 | 描述 |
|------|------|------|--------|------|
| `opts.rate` | Number | 否 | `1` | 缩放比例,影响宽度和高度 |
| `opts.url` | String | 否 | - | SVG 文件的 URL 地址 |
| `opts.blink` | Boolean | 否 | `false` | 是否启用闪烁效果 |
| `opts.color` | String (CSS Color) | 否 | 由 `bricks.app.get_color()` 获取 | SVG 填充颜色 |
| `opts.blinkcolor` | String (CSS Color) | 否 | 由 `bricks.app.get_bgcolor()` 获取 | 闪烁时的颜色 |
| `opts.blinktime` | Number | 否 | `0.5` | 闪烁间隔时间(秒) |
> ⚠️ 注意:`opts.cwidth``opts.cheight` 将被自动设为 `opts.rate`,实现等比缩放。
#### 行为说明
- 调用父类 `super(opts)` 初始化容器。
- 若未指定 `color``blinkcolor`,则使用应用级默认前景/背景色。
- 若提供了 `url`,调用 `set_url(url)` 加载 SVG 内容。
- 支持动态尺寸(`dynsize: true`)。
---
### 方法列表
#### `set_url(url)`
从指定 URL 加载 SVG 内容并插入到 DOM。
##### 参数
- `url` (String | null)SVG 文件路径或网址;若为 `null` 或空,则清空内容。
##### 流程
1. 清除当前内容(若无 `url`)。
2. 使用 `fetch()` 请求 SVG 文本。
3. 验证响应是否以 `<svg ` 开头(防止 XSS
4. 存储原始 SVG 文本至 `this.svgText`
5. 调用 `set_colored_svg(color)` 渲染带颜色的 SVG。
6. 如果启用了 `blink`,启动闪烁动画。
> ✅ 安全提示:仅允许标准 `<svg ...>` 标签开头的内容,避免注入风险。
---
#### `set_color(color)`
设置 SVG 的填充颜色,并立即重新渲染。
##### 参数
- `color` (String):合法 CSS 颜色值(如 `"red"`, `"#00ff00"`, `"rgb(255,0,0)"`
##### 行为
- 更新 `this.color`
- 调用 `set_colored_svg(color)`
---
#### `set_blinkcolor(color)`
设置闪烁状态下的颜色。
##### 参数
- `color` (String):目标闪烁颜色
##### 行为
- 更新 `this.blinkcolor`
- 自动补全 `blinktime``0.5`(如果尚未设置)
---
#### `set_colored_svg(color)`
将模板中的 `{color}` 占位符替换为实际颜色值,并更新 DOM。
##### 参数
- `color` (String):要应用的颜色
##### 实现
```js
var svgText = bricks.obj_fmtstr({color: color}, this.svgText);
this.dom_element.innerHTML = svgText;
```
> 📌 依赖 `bricks.obj_fmtstr` 实现字符串模板替换(类似 `{color}``"#ff0000"`
---
#### `start_blink()`
启动周期性颜色闪烁动画。
##### 条件
- 必须设置了 `blinktime > 0`
- 必须定义了 `blinkcolor`
##### 行为
- 若当前没有运行任务(`!this.blink_task`),调用 `_blink()` 启动首次切换。
---
#### `_blink()`
私有方法:执行一次颜色切换,并安排下一次调用。
##### 逻辑
- 当前颜色是正常色 → 切换为 `blinkcolor`
- 当前颜色是 `blinkcolor` → 切换回正常 `color`
- 使用 `schedule_once(fn, delayInSeconds)` 安排下次执行(避免阻塞主线程)
> 🔁 此方法形成递归循环,直到 `end_blink()` 被调用。
---
#### `end_blink()`
停止闪烁动画。
##### 行为
- 设置 `this.blink_task = null`,中断 `_blink()` 的递归调度链。
---
#### `set_ancent_color(e)`
从父元素的计算样式中获取文本颜色,并设置为 SVG 颜色。
##### 应用场景
适用于希望图标跟随上下文主题色的情况。
##### 实现
```js
var pstyle = getComputedStyle(this.parent);
this.set_color(pstyle.color);
```
---
## 2. `bricks.StatedSvg`
### 简介
继承自 `bricks.Svg`支持多个状态state每个状态对应不同的 SVG 资源。点击可按顺序切换状态。
### 构造函数:`constructor(opts)`
#### 扩展参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.states` | Array<{state: String, url: String}> | 是 | 状态数组,每项包含状态名与对应的 SVG URL |
| `opts.state` | String | 否 | 初始状态;若不提供,默认取第一个状态 |
#### 初始化行为
- 设置初始状态 `this.curstate`
- 调用 `set_state(state)` 加载对应 SVG
- 绑定 `click` 事件处理器 `trigger`
---
### 方法列表
#### `trigger(event)`
处理点击事件,顺序切换到下一个状态(循环)。
##### 事件处理
- `stopPropagation()``preventDefault()` 阻止冒泡与默认行为
##### 切换逻辑
- 查找当前状态索引 `i`
- 下一状态索引:`k = i + 1`,若已达末尾则回到 `0`
- 调用 `set_state(states[k].state)`
- 触发 `state_changed` 事件
---
#### `set_state(state)`
切换到指定状态。
##### 参数
- `state` (String):目标状态名称
##### 行为
- 若已处于该状态,直接返回
- 遍历 `this.states` 查找匹配项
- 找到后调用 `set_url(s.url)`
- 广播 `state_changed` 事件
- 若未找到匹配项,清除 SVG 内容(`set_url(null)`
---
## 3. `bricks.MultipleStateIcon`
### 简介
另一种多状态图标实现方式,使用对象字典管理 URL 映射,适合命名清晰的状态切换(如开关、模式选择等)。
### 构造函数:`constructor(opts)`
#### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.urls` | Object<String, String> | 是 | 状态名到 SVG URL 的映射表,例如 `{on: 'on.svg', off: 'off.svg'}` |
| `opts.state` | String | 是 | 初始状态键名 |
#### 初始化流程
- 从 `urls[state]` 提取初始 URL
- 调用 `super(opts)` 创建基础 SVG
- 记录 `this.urls` 和当前 `this.state`
- 绑定 `click` 事件到 `change_state`
---
### 方法列表
#### `change_state(event)`
点击时切换到下一个状态(按 `Object.keys(urls)` 顺序循环)。
##### 流程
- 获取所有状态键名数组
- 查找当前 `state` 的索引
- 计算下一索引(越界则归零)
- 调用 `set_state(newState)`
- 触发 `state_changed` 事件
---
#### `set_state(state)`
设置当前状态并加载对应 SVG。
##### 参数
- `state` (String):目标状态键名
##### 行为
- 更新 `this.state`
- 调用 `set_url(this.urls[state])`
---
## 工厂注册
以下三类组件均已向 `bricks.Factory` 注册,可在声明式模板中使用:
```js
bricks.Factory.register('Svg', bricks.Svg);
bricks.Factory.register('StatedSvg', bricks.StatedSvg);
bricks.Factory.register('MultipleStateIcon', bricks.MultipleStateIcon);
```
这意味着可以通过如下方式创建实例(假设框架支持):
```html
<div data-widget="Svg" data-options='{"url": "icon.svg", "color": "blue"}'></div>
```
---
## 使用示例
### 示例 1基础彩色 SVG
```js
const icon = new bricks.Svg({
url: '/icons/home.svg',
color: 'green',
blink: true,
blinkcolor: 'yellow',
blinktime: 0.8
});
document.body.appendChild(icon.dom_element);
```
### 示例 2三态切换图标StatedSvg
```js
const light = new bricks.StatedSvg({
states: [
{ state: 'red', url: '/svg/red.svg' },
{ state: 'yellow', url: '/svg/yellow.svg' },
{ state: 'green', url: '/svg/green.svg' }
],
state: 'red'
});
light.on('state_changed', (state) => {
console.log('Current state:', state);
});
```
### 示例 3双态图标MultipleStateIcon
```js
const toggle = new bricks.MultipleStateIcon({
urls: {
on: '/icons/power-on.svg',
off: '/icons/power-off.svg'
},
state: 'off'
});
toggle.on('state_changed', s => console.log('Power is now:', s));
```
---
## 注意事项与最佳实践
| 项目 | 建议 |
|------|------|
| **安全性** | 不要加载不可信来源的 SVG尤其是含 `<script>` 或内联事件的文件 |
| **性能** | 多次调用 `set_color` 会触发重绘,建议批量操作 |
| **闪烁控制** | 使用 `start_blink()` / `end_blink()` 显式控制动画生命周期 |
| **异步加载** | `set_url()` 是异步操作,后续逻辑应放在监听器或 Promise 中处理 |
| **颜色同步** | 推荐使用 `set_ancent_color()` 实现主题适配 |
---
## 依赖说明
| 依赖项 | 来源 | 用途 |
|--------|------|------|
| `bricks.VBox` | 基础布局容器 | 提供 DOM 容器能力 |
| `bricks.app.get_color()` | 应用配置模块 | 获取默认前景色 |
| `bricks.app.get_bgcolor()` | 应用配置模块 | 获取默认背景色 |
| `bricks.obj_fmtstr()` | 字符串工具 | 替换 `{color}` 模板 |
| `schedule_once(fn, sec)` | 异步调度工具 | 实现非阻塞定时任务 |
---
## 版本信息
- 编写日期2025年4月5日
- 框架版本兼容性BricksJS v1.x+
- 维护者:前端组件团队
---
**文档完成**
如有扩展需求,请参考现有接口进行子类化开发。

Some files were not shown because too many files have changed in this diff Show More