首次完整推送,

V:1.20240808.006
This commit is contained in:
fm453
2024-08-13 18:32:37 +08:00
parent 15be3e9373
commit c62d15b288
939 changed files with 111777 additions and 0 deletions

View File

@ -0,0 +1,12 @@
## 4.0.62022-12-12
修复`getDrawModules`,第一次获取结果正常,后续获取`tile`模块不存在的问题;
修复安卓type:normal因Canvas API使用了小数或为0的参数导致生成异常的问题安卓非2d Canvas部分API参数不支持携带小数部分API参数必须大于0
## 4.0.12022-11-28
优化组件loading属性的表现
新增组件type选项normal以便于在某些条件编译初始为type=2d时还可以选择使用非2d组件类型
修复组件条件编译在其他编辑器语法提示报错;
修复原生对es5的支持。
## 4.0.02022-11-21
v4版本源代码全面开放开源地址[https://github.com/Sansnn/uQRCode](https://github.com/Sansnn/uQRCode)
升级说明v4为大版本更新虽然已尽可能兼容上一代版本但不可避免的还是存在一些细节差异若更新后出现问题请参考对照[v3 文档](https://uqrcode.cn/doc/v3)[v4 文档](https://uqrcode.cn/doc)进行修改。

View File

@ -0,0 +1 @@
export const cacheImageList = [];

View File

@ -0,0 +1,41 @@
function Queue() {
let waitingQueue = this.waitingQueue = [];
let isRunning = this.isRunning = false; // 记录是否有未完成的任务
function execute(task, resolve, reject) {
task()
.then((data) => {
resolve(data);
})
.catch((e) => {
reject(e);
})
.finally(() => {
// 等待任务队列中如果有任务则触发它否则设置isRunning = false,表示无任务状态
if (waitingQueue.length) {
const next = waitingQueue.shift();
execute(next.task, next.resolve, next.reject);
} else {
isRunning = false;
}
});
}
this.exec = function(task) {
return new Promise((resolve, reject) => {
if (isRunning) {
waitingQueue.push({
task,
resolve,
reject
});
} else {
isRunning = true;
execute(task, resolve, reject);
}
});
}
}
/* 队列实例某些平台一起使用多个组件时需要通过队列逐一绘制否则部分绘制方法异常nvue端的iOS gcanvas尤其明显在不通过队列绘制时会出现图片丢失的情况 */
export const queueDraw = new Queue();
export const queueLoadImage = new Queue();

View File

@ -0,0 +1,3 @@
declare module '*/common/cache' {
export const cacheImageList: Array;
}

View File

@ -0,0 +1,4 @@
declare module '*/common/queue' {
export const queueDraw: any;
export const queueLoadImage: any;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,241 @@
const isWeex = typeof WXEnvironment !== 'undefined';
const isWeexIOS = isWeex && /ios/i.test(WXEnvironment.platform);
const isWeexAndroid = isWeex && !isWeexIOS;
import GLmethod from '../context-webgl/GLmethod';
const GCanvasModule =
(typeof weex !== 'undefined' && weex.requireModule) ? (weex.requireModule('gcanvas')) :
(typeof __weex_require__ !== 'undefined') ? (__weex_require__('@weex-module/gcanvas')) : {};
let isDebugging = false;
let isComboDisabled = false;
const logCommand = (function () {
const methodQuery = [];
Object.keys(GLmethod).forEach(key => {
methodQuery[GLmethod[key]] = key;
})
const queryMethod = (id) => {
return methodQuery[parseInt(id)] || 'NotFoundMethod';
}
const logCommand = (id, cmds) => {
const mId = cmds.split(',')[0];
const mName = queryMethod(mId);
console.log(`=== callNative - componentId:${id}; method: ${mName}; cmds: ${cmds}`);
}
return logCommand;
})();
function joinArray(arr, sep) {
let res = '';
for (let i = 0; i < arr.length; i++) {
if (i !== 0) {
res += sep;
}
res += arr[i];
}
return res;
}
const commandsCache = {}
const GBridge = {
callEnable: (ref, configArray) => {
commandsCache[ref] = [];
return GCanvasModule.enable({
componentId: ref,
config: configArray
});
},
callEnableDebug: () => {
isDebugging = true;
},
callEnableDisableCombo: () => {
isComboDisabled = true;
},
callSetContextType: function (componentId, context_type) {
GCanvasModule.setContextType(context_type, componentId);
},
callReset: function(id){
GCanvasModule.resetComponent && canvasModule.resetComponent(componentId);
},
render: isWeexIOS ? function (componentId) {
return GCanvasModule.extendCallNative({
contextId: componentId,
type: 0x60000001
});
} : function (componentId) {
return callGCanvasLinkNative(componentId, 0x60000001, 'render');
},
render2d: isWeexIOS ? function (componentId, commands, callback) {
if (isDebugging) {
console.log('>>> >>> render2d ===');
console.log('>>> commands: ' + commands);
}
GCanvasModule.render([commands, callback?true:false], componentId, callback);
} : function (componentId, commands,callback) {
if (isDebugging) {
console.log('>>> >>> render2d ===');
console.log('>>> commands: ' + commands);
}
callGCanvasLinkNative(componentId, 0x20000001, commands);
if(callback){
callback();
}
},
callExtendCallNative: isWeexIOS ? function (componentId, cmdArgs) {
throw 'should not be here anymore ' + cmdArgs;
} : function (componentId, cmdArgs) {
throw 'should not be here anymore ' + cmdArgs;
},
flushNative: isWeexIOS ? function (componentId) {
const cmdArgs = joinArray(commandsCache[componentId], ';');
commandsCache[componentId] = [];
if (isDebugging) {
console.log('>>> >>> flush native ===');
console.log('>>> commands: ' + cmdArgs);
}
const result = GCanvasModule.extendCallNative({
"contextId": componentId,
"type": 0x60000000,
"args": cmdArgs
});
const res = result && result.result;
if (isDebugging) {
console.log('>>> result: ' + res);
}
return res;
} : function (componentId) {
const cmdArgs = joinArray(commandsCache[componentId], ';');
commandsCache[componentId] = [];
if (isDebugging) {
console.log('>>> >>> flush native ===');
console.log('>>> commands: ' + cmdArgs);
}
const result = callGCanvasLinkNative(componentId, 0x60000000, cmdArgs);
if (isDebugging) {
console.log('>>> result: ' + result);
}
return result;
},
callNative: function (componentId, cmdArgs, cache) {
if (isDebugging) {
logCommand(componentId, cmdArgs);
}
commandsCache[componentId].push(cmdArgs);
if (!cache || isComboDisabled) {
return GBridge.flushNative(componentId);
} else {
return undefined;
}
},
texImage2D(componentId, ...args) {
if (isWeexIOS) {
if (args.length === 6) {
const [target, level, internalformat, format, type, image] = args;
GBridge.callNative(
componentId,
GLmethod.texImage2D + ',' + 6 + ',' + target + ',' + level + ',' + internalformat + ',' + format + ',' + type + ',' + image.src
)
} else if (args.length === 9) {
const [target, level, internalformat, width, height, border, format, type, image] = args;
GBridge.callNative(
componentId,
GLmethod.texImage2D + ',' + 9 + ',' + target + ',' + level + ',' + internalformat + ',' + width + ',' + height + ',' + border + ',' +
+ format + ',' + type + ',' + (image ? image.src : 0)
)
}
} else if (isWeexAndroid) {
if (args.length === 6) {
const [target, level, internalformat, format, type, image] = args;
GCanvasModule.texImage2D(componentId, target, level, internalformat, format, type, image.src);
} else if (args.length === 9) {
const [target, level, internalformat, width, height, border, format, type, image] = args;
GCanvasModule.texImage2D(componentId, target, level, internalformat, width, height, border, format, type, (image ? image.src : 0));
}
}
},
texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image) {
if (isWeexIOS) {
if (arguments.length === 8) {
GBridge.callNative(
componentId,
GLmethod.texSubImage2D + ',' + 6 + ',' + target + ',' + level + ',' + xoffset + ',' + yoffset, + ',' + format + ',' + type + ',' + image.src
)
}
} else if (isWeexAndroid) {
GCanvasModule.texSubImage2D(componentId, target, level, xoffset, yoffset, format, type, image.src);
}
},
bindImageTexture(componentId, src, imageId) {
GCanvasModule.bindImageTexture([src, imageId], componentId);
},
perloadImage([url, id], callback) {
GCanvasModule.preLoadImage([url, id], function (image) {
image.url = url;
image.id = id;
callback(image);
});
},
measureText(text, fontStyle, componentId) {
return GCanvasModule.measureText([text, fontStyle], componentId);
},
getImageData (componentId, x, y, w, h, callback) {
GCanvasModule.getImageData([x, y,w,h],componentId,callback);
},
putImageData (componentId, data, x, y, w, h, callback) {
GCanvasModule.putImageData([x, y,w,h,data],componentId,callback);
},
toTempFilePath(componentId, x, y, width, height, destWidth, destHeight, fileType, quality, callback){
GCanvasModule.toTempFilePath([x, y, width,height, destWidth, destHeight, fileType, quality], componentId, callback);
}
}
export default GBridge;

View File

@ -0,0 +1,18 @@
class FillStyleLinearGradient {
constructor(x0, y0, x1, y1) {
this._start_pos = { _x: x0, _y: y0 };
this._end_pos = { _x: x1, _y: y1 };
this._stop_count = 0;
this._stops = [0, 0, 0, 0, 0];
}
addColorStop = function (pos, color) {
if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
this._stops[this._stop_count] = { _pos: pos, _color: color };
this._stop_count++;
}
}
}
export default FillStyleLinearGradient;

View File

@ -0,0 +1,8 @@
class FillStylePattern {
constructor(img, pattern) {
this._style = pattern;
this._img = img;
}
}
export default FillStylePattern;

View File

@ -0,0 +1,17 @@
class FillStyleRadialGradient {
constructor(x0, y0, r0, x1, y1, r1) {
this._start_pos = { _x: x0, _y: y0, _r: r0 };
this._end_pos = { _x: x1, _y: y1, _r: r1 };
this._stop_count = 0;
this._stops = [0, 0, 0, 0, 0];
}
addColorStop(pos, color) {
if (this._stop_count < 5 && 0.0 <= pos && pos <= 1.0) {
this._stops[this._stop_count] = { _pos: pos, _color: color };
this._stop_count++;
}
}
}
export default FillStyleRadialGradient;

View File

@ -0,0 +1,666 @@
import FillStylePattern from './FillStylePattern';
import FillStyleLinearGradient from './FillStyleLinearGradient';
import FillStyleRadialGradient from './FillStyleRadialGradient';
import GImage from '../env/image.js';
import {
ArrayBufferToBase64,
Base64ToUint8ClampedArray
} from '../env/tool.js';
export default class CanvasRenderingContext2D {
_drawCommands = '';
_globalAlpha = 1.0;
_fillStyle = 'rgb(0,0,0)';
_strokeStyle = 'rgb(0,0,0)';
_lineWidth = 1;
_lineCap = 'butt';
_lineJoin = 'miter';
_miterLimit = 10;
_globalCompositeOperation = 'source-over';
_textAlign = 'start';
_textBaseline = 'alphabetic';
_font = '10px sans-serif';
_savedGlobalAlpha = [];
timer = null;
componentId = null;
_notCommitDrawImageCache = [];
_needRedrawImageCache = [];
_redrawCommands = '';
_autoSaveContext = true;
// _imageMap = new GHashMap();
// _textureMap = new GHashMap();
constructor() {
this.className = 'CanvasRenderingContext2D';
//this.save()
}
setFillStyle(value) {
this.fillStyle = value;
}
set fillStyle(value) {
this._fillStyle = value;
if (typeof(value) == 'string') {
this._drawCommands = this._drawCommands.concat("F" + value + ";");
} else if (value instanceof FillStylePattern) {
const image = value._img;
if (!image.complete) {
image.onload = () => {
var index = this._needRedrawImageCache.indexOf(image);
if (index > -1) {
this._needRedrawImageCache.splice(index, 1);
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._redrawflush(true);
}
}
this._notCommitDrawImageCache.push(image);
} else {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
}
//CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
} else if (value instanceof FillStyleLinearGradient) {
var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
} else if (value instanceof FillStyleRadialGradient) {
var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," + value._end_pos._r.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
}
}
get fillStyle() {
return this._fillStyle;
}
get globalAlpha() {
return this._globalAlpha;
}
setGlobalAlpha(value) {
this.globalAlpha = value;
}
set globalAlpha(value) {
this._globalAlpha = value;
this._drawCommands = this._drawCommands.concat("a" + value.toFixed(2) + ";");
}
get strokeStyle() {
return this._strokeStyle;
}
setStrokeStyle(value) {
this.strokeStyle = value;
}
set strokeStyle(value) {
this._strokeStyle = value;
if (typeof(value) == 'string') {
this._drawCommands = this._drawCommands.concat("S" + value + ";");
} else if (value instanceof FillStylePattern) {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._drawCommands = this._drawCommands.concat("G" + image._id + "," + value._style + ";");
} else if (value instanceof FillStyleLinearGradient) {
var command = "D" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
} else if (value instanceof FillStyleRadialGradient) {
var command = "H" + value._start_pos._x.toFixed(2) + "," + value._start_pos._y.toFixed(2) + "," + value._start_pos._r
.toFixed(2) + "," +
value._end_pos._x.toFixed(2) + "," + value._end_pos._y + ",".toFixed(2) + value._end_pos._r.toFixed(2) + "," +
value._stop_count;
for (var i = 0; i < value._stop_count; ++i) {
command += ("," + value._stops[i]._pos + "," + value._stops[i]._color);
}
this._drawCommands = this._drawCommands.concat(command + ";");
}
}
get lineWidth() {
return this._lineWidth;
}
setLineWidth(value) {
this.lineWidth = value;
}
set lineWidth(value) {
this._lineWidth = value;
this._drawCommands = this._drawCommands.concat("W" + value + ";");
}
get lineCap() {
return this._lineCap;
}
setLineCap(value) {
this.lineCap = value;
}
set lineCap(value) {
this._lineCap = value;
this._drawCommands = this._drawCommands.concat("C" + value + ";");
}
get lineJoin() {
return this._lineJoin;
}
setLineJoin(value) {
this.lineJoin = value
}
set lineJoin(value) {
this._lineJoin = value;
this._drawCommands = this._drawCommands.concat("J" + value + ";");
}
get miterLimit() {
return this._miterLimit;
}
setMiterLimit(value) {
this.miterLimit = value
}
set miterLimit(value) {
this._miterLimit = value;
this._drawCommands = this._drawCommands.concat("M" + value + ";");
}
get globalCompositeOperation() {
return this._globalCompositeOperation;
}
set globalCompositeOperation(value) {
this._globalCompositeOperation = value;
let mode = 0;
switch (value) {
case "source-over":
mode = 0;
break;
case "source-atop":
mode = 5;
break;
case "source-in":
mode = 0;
break;
case "source-out":
mode = 2;
break;
case "destination-over":
mode = 4;
break;
case "destination-atop":
mode = 4;
break;
case "destination-in":
mode = 4;
break;
case "destination-out":
mode = 3;
break;
case "lighter":
mode = 1;
break;
case "copy":
mode = 2;
break;
case "xor":
mode = 6;
break;
default:
mode = 0;
}
this._drawCommands = this._drawCommands.concat("B" + mode + ";");
}
get textAlign() {
return this._textAlign;
}
setTextAlign(value) {
this.textAlign = value
}
set textAlign(value) {
this._textAlign = value;
let Align = 0;
switch (value) {
case "start":
Align = 0;
break;
case "end":
Align = 1;
break;
case "left":
Align = 2;
break;
case "center":
Align = 3;
break;
case "right":
Align = 4;
break;
default:
Align = 0;
}
this._drawCommands = this._drawCommands.concat("A" + Align + ";");
}
get textBaseline() {
return this._textBaseline;
}
setTextBaseline(value) {
this.textBaseline = value
}
set textBaseline(value) {
this._textBaseline = value;
let baseline = 0;
switch (value) {
case "alphabetic":
baseline = 0;
break;
case "middle":
baseline = 1;
break;
case "top":
baseline = 2;
break;
case "hanging":
baseline = 3;
break;
case "bottom":
baseline = 4;
break;
case "ideographic":
baseline = 5;
break;
default:
baseline = 0;
break;
}
this._drawCommands = this._drawCommands.concat("E" + baseline + ";");
}
get font() {
return this._font;
}
setFontSize(size) {
var str = this._font;
var strs = str.trim().split(/\s+/);
for (var i = 0; i < strs.length; i++) {
var values = ["normal", "italic", "oblique", "normal", "small-caps", "normal", "bold",
"bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900",
"normal", "ultra-condensed", "extra-condensed", "condensed", "semi-condensed",
"semi-expanded", "expanded", "extra-expanded", "ultra-expanded"
];
if (-1 == values.indexOf(strs[i].trim())) {
if (typeof size === 'string') {
strs[i] = size;
} else if (typeof size === 'number') {
strs[i] = String(size) + 'px';
}
break;
}
}
this.font = strs.join(" ");
}
set font(value) {
this._font = value;
this._drawCommands = this._drawCommands.concat("j" + value + ";");
}
setTransform(a, b, c, d, tx, ty) {
this._drawCommands = this._drawCommands.concat("t" +
(a === 1 ? "1" : a.toFixed(2)) + "," +
(b === 0 ? "0" : b.toFixed(2)) + "," +
(c === 0 ? "0" : c.toFixed(2)) + "," +
(d === 1 ? "1" : d.toFixed(2)) + "," + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
}
transform(a, b, c, d, tx, ty) {
this._drawCommands = this._drawCommands.concat("f" +
(a === 1 ? "1" : a.toFixed(2)) + "," +
(b === 0 ? "0" : b.toFixed(2)) + "," +
(c === 0 ? "0" : c.toFixed(2)) + "," +
(d === 1 ? "1" : d.toFixed(2)) + "," + tx + "," + ty + ";");
}
resetTransform() {
this._drawCommands = this._drawCommands.concat("m;");
}
scale(a, d) {
this._drawCommands = this._drawCommands.concat("k" + a.toFixed(2) + "," +
d.toFixed(2) + ";");
}
rotate(angle) {
this._drawCommands = this._drawCommands
.concat("r" + angle.toFixed(6) + ";");
}
translate(tx, ty) {
this._drawCommands = this._drawCommands.concat("l" + tx.toFixed(2) + "," + ty.toFixed(2) + ";");
}
save() {
this._savedGlobalAlpha.push(this._globalAlpha);
this._drawCommands = this._drawCommands.concat("v;");
}
restore() {
this._drawCommands = this._drawCommands.concat("e;");
this._globalAlpha = this._savedGlobalAlpha.pop();
}
createPattern(img, pattern) {
if (typeof img === 'string') {
var imgObj = new GImage();
imgObj.src = img;
img = imgObj;
}
return new FillStylePattern(img, pattern);
}
createLinearGradient(x0, y0, x1, y1) {
return new FillStyleLinearGradient(x0, y0, x1, y1);
}
createRadialGradient = function(x0, y0, r0, x1, y1, r1) {
return new FillStyleRadialGradient(x0, y0, r0, x1, y1, r1);
};
createCircularGradient = function(x0, y0, r0) {
return new FillStyleRadialGradient(x0, y0, 0, x0, y0, r0);
};
strokeRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("s" + x + "," + y + "," + w + "," + h + ";");
}
clearRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("c" + x + "," + y + "," + w +
"," + h + ";");
}
clip() {
this._drawCommands = this._drawCommands.concat("p;");
}
resetClip() {
this._drawCommands = this._drawCommands.concat("q;");
}
closePath() {
this._drawCommands = this._drawCommands.concat("o;");
}
moveTo(x, y) {
this._drawCommands = this._drawCommands.concat("g" + x.toFixed(2) + "," + y.toFixed(2) + ";");
}
lineTo(x, y) {
this._drawCommands = this._drawCommands.concat("i" + x.toFixed(2) + "," + y.toFixed(2) + ";");
}
quadraticCurveTo = function(cpx, cpy, x, y) {
this._drawCommands = this._drawCommands.concat("u" + cpx + "," + cpy + "," + x + "," + y + ";");
}
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, ) {
this._drawCommands = this._drawCommands.concat(
"z" + cp1x.toFixed(2) + "," + cp1y.toFixed(2) + "," + cp2x.toFixed(2) + "," + cp2y.toFixed(2) + "," +
x.toFixed(2) + "," + y.toFixed(2) + ";");
}
arcTo(x1, y1, x2, y2, radius) {
this._drawCommands = this._drawCommands.concat("h" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + radius + ";");
}
beginPath() {
this._drawCommands = this._drawCommands.concat("b;");
}
fillRect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("n" + x + "," + y + "," + w +
"," + h + ";");
}
rect(x, y, w, h) {
this._drawCommands = this._drawCommands.concat("w" + x + "," + y + "," + w + "," + h + ";");
}
fill() {
this._drawCommands = this._drawCommands.concat("L;");
}
stroke(path) {
this._drawCommands = this._drawCommands.concat("x;");
}
arc(x, y, radius, startAngle, endAngle, anticlockwise) {
let ianticlockwise = 0;
if (anticlockwise) {
ianticlockwise = 1;
}
this._drawCommands = this._drawCommands.concat(
"y" + x.toFixed(2) + "," + y.toFixed(2) + "," +
radius.toFixed(2) + "," + startAngle + "," + endAngle + "," + ianticlockwise +
";"
);
}
fillText(text, x, y) {
let tmptext = text.replace(/!/g, "!!");
tmptext = tmptext.replace(/,/g, "!,");
tmptext = tmptext.replace(/;/g, "!;");
this._drawCommands = this._drawCommands.concat("T" + tmptext + "," + x + "," + y + ",0.0;");
}
strokeText = function(text, x, y) {
let tmptext = text.replace(/!/g, "!!");
tmptext = tmptext.replace(/,/g, "!,");
tmptext = tmptext.replace(/;/g, "!;");
this._drawCommands = this._drawCommands.concat("U" + tmptext + "," + x + "," + y + ",0.0;");
}
measureText(text) {
return CanvasRenderingContext2D.GBridge.measureText(text, this.font, this.componentId);
}
isPointInPath = function(x, y) {
throw new Error('GCanvas not supported yet');
}
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
if (typeof image === 'string') {
var imgObj = new GImage();
imgObj.src = image;
image = imgObj;
}
if (image instanceof GImage) {
if (!image.complete) {
imgObj.onload = () => {
var index = this._needRedrawImageCache.indexOf(image);
if (index > -1) {
this._needRedrawImageCache.splice(index, 1);
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
this._redrawflush(true);
}
}
this._notCommitDrawImageCache.push(image);
} else {
CanvasRenderingContext2D.GBridge.bindImageTexture(this.componentId, image.src, image._id);
}
var srcArgs = [image, sx, sy, sw, sh, dx, dy, dw, dh];
var args = [];
for (var arg in srcArgs) {
if (typeof(srcArgs[arg]) != 'undefined') {
args.push(srcArgs[arg]);
}
}
this.__drawImage.apply(this, args);
//this.__drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
}
}
__drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) {
const numArgs = arguments.length;
function drawImageCommands() {
if (numArgs === 3) {
const x = parseFloat(sx) || 0.0;
const y = parseFloat(sy) || 0.0;
return ("d" + image._id + ",0,0," +
image.width + "," + image.height + "," +
x + "," + y + "," + image.width + "," + image.height + ";");
} else if (numArgs === 5) {
const x = parseFloat(sx) || 0.0;
const y = parseFloat(sy) || 0.0;
const width = parseInt(sw) || image.width;
const height = parseInt(sh) || image.height;
return ("d" + image._id + ",0,0," +
image.width + "," + image.height + "," +
x + "," + y + "," + width + "," + height + ";");
} else if (numArgs === 9) {
sx = parseFloat(sx) || 0.0;
sy = parseFloat(sy) || 0.0;
sw = parseInt(sw) || image.width;
sh = parseInt(sh) || image.height;
dx = parseFloat(dx) || 0.0;
dy = parseFloat(dy) || 0.0;
dw = parseInt(dw) || image.width;
dh = parseInt(dh) || image.height;
return ("d" + image._id + "," +
sx + "," + sy + "," + sw + "," + sh + "," +
dx + "," + dy + "," + dw + "," + dh + ";");
}
}
this._drawCommands += drawImageCommands();
}
_flush(reserve, callback) {
const commands = this._drawCommands;
this._drawCommands = '';
CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
this._needRender = false;
}
_redrawflush(reserve, callback) {
const commands = this._redrawCommands;
CanvasRenderingContext2D.GBridge.render2d(this.componentId, commands, callback);
if (this._needRedrawImageCache.length == 0) {
this._redrawCommands = '';
}
}
draw(reserve, callback) {
if (!reserve) {
this._globalAlpha = this._savedGlobalAlpha.pop();
this._savedGlobalAlpha.push(this._globalAlpha);
this._redrawCommands = this._drawCommands;
this._needRedrawImageCache = this._notCommitDrawImageCache;
if (this._autoSaveContext) {
this._drawCommands = ("v;" + this._drawCommands);
this._autoSaveContext = false;
} else {
this._drawCommands = ("e;X;v;" + this._drawCommands);
}
} else {
this._needRedrawImageCache = this._needRedrawImageCache.concat(this._notCommitDrawImageCache);
this._redrawCommands += this._drawCommands;
if (this._autoSaveContext) {
this._drawCommands = ("v;" + this._drawCommands);
this._autoSaveContext = false;
}
}
this._notCommitDrawImageCache = [];
if (this._flush) {
this._flush(reserve, callback);
}
}
getImageData(x, y, w, h, callback) {
CanvasRenderingContext2D.GBridge.getImageData(this.componentId, x, y, w, h, function(res) {
res.data = Base64ToUint8ClampedArray(res.data);
if (typeof(callback) == 'function') {
callback(res);
}
});
}
putImageData(data, x, y, w, h, callback) {
if (data instanceof Uint8ClampedArray) {
data = ArrayBufferToBase64(data);
CanvasRenderingContext2D.GBridge.putImageData(this.componentId, data, x, y, w, h, function(res) {
if (typeof(callback) == 'function') {
callback(res);
}
});
}
}
toTempFilePath(x, y, width, height, destWidth, destHeight, fileType, quality, callback) {
CanvasRenderingContext2D.GBridge.toTempFilePath(this.componentId, x, y, width, height, destWidth, destHeight,
fileType, quality,
function(res) {
if (typeof(callback) == 'function') {
callback(res);
}
});
}
}

View File

@ -0,0 +1,11 @@
export default class WebGLActiveInfo {
className = 'WebGLActiveInfo';
constructor({
type, name, size
}) {
this.type = type;
this.name = name;
this.size = size;
}
}

View File

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLBuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

View File

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLFrameBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLFramebuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

View File

@ -0,0 +1,298 @@
export default {
"DEPTH_BUFFER_BIT": 256,
"STENCIL_BUFFER_BIT": 1024,
"COLOR_BUFFER_BIT": 16384,
"POINTS": 0,
"LINES": 1,
"LINE_LOOP": 2,
"LINE_STRIP": 3,
"TRIANGLES": 4,
"TRIANGLE_STRIP": 5,
"TRIANGLE_FAN": 6,
"ZERO": 0,
"ONE": 1,
"SRC_COLOR": 768,
"ONE_MINUS_SRC_COLOR": 769,
"SRC_ALPHA": 770,
"ONE_MINUS_SRC_ALPHA": 771,
"DST_ALPHA": 772,
"ONE_MINUS_DST_ALPHA": 773,
"DST_COLOR": 774,
"ONE_MINUS_DST_COLOR": 775,
"SRC_ALPHA_SATURATE": 776,
"FUNC_ADD": 32774,
"BLEND_EQUATION": 32777,
"BLEND_EQUATION_RGB": 32777,
"BLEND_EQUATION_ALPHA": 34877,
"FUNC_SUBTRACT": 32778,
"FUNC_REVERSE_SUBTRACT": 32779,
"BLEND_DST_RGB": 32968,
"BLEND_SRC_RGB": 32969,
"BLEND_DST_ALPHA": 32970,
"BLEND_SRC_ALPHA": 32971,
"CONSTANT_COLOR": 32769,
"ONE_MINUS_CONSTANT_COLOR": 32770,
"CONSTANT_ALPHA": 32771,
"ONE_MINUS_CONSTANT_ALPHA": 32772,
"BLEND_COLOR": 32773,
"ARRAY_BUFFER": 34962,
"ELEMENT_ARRAY_BUFFER": 34963,
"ARRAY_BUFFER_BINDING": 34964,
"ELEMENT_ARRAY_BUFFER_BINDING": 34965,
"STREAM_DRAW": 35040,
"STATIC_DRAW": 35044,
"DYNAMIC_DRAW": 35048,
"BUFFER_SIZE": 34660,
"BUFFER_USAGE": 34661,
"CURRENT_VERTEX_ATTRIB": 34342,
"FRONT": 1028,
"BACK": 1029,
"FRONT_AND_BACK": 1032,
"TEXTURE_2D": 3553,
"CULL_FACE": 2884,
"BLEND": 3042,
"DITHER": 3024,
"STENCIL_TEST": 2960,
"DEPTH_TEST": 2929,
"SCISSOR_TEST": 3089,
"POLYGON_OFFSET_FILL": 32823,
"SAMPLE_ALPHA_TO_COVERAGE": 32926,
"SAMPLE_COVERAGE": 32928,
"NO_ERROR": 0,
"INVALID_ENUM": 1280,
"INVALID_VALUE": 1281,
"INVALID_OPERATION": 1282,
"OUT_OF_MEMORY": 1285,
"CW": 2304,
"CCW": 2305,
"LINE_WIDTH": 2849,
"ALIASED_POINT_SIZE_RANGE": 33901,
"ALIASED_LINE_WIDTH_RANGE": 33902,
"CULL_FACE_MODE": 2885,
"FRONT_FACE": 2886,
"DEPTH_RANGE": 2928,
"DEPTH_WRITEMASK": 2930,
"DEPTH_CLEAR_VALUE": 2931,
"DEPTH_FUNC": 2932,
"STENCIL_CLEAR_VALUE": 2961,
"STENCIL_FUNC": 2962,
"STENCIL_FAIL": 2964,
"STENCIL_PASS_DEPTH_FAIL": 2965,
"STENCIL_PASS_DEPTH_PASS": 2966,
"STENCIL_REF": 2967,
"STENCIL_VALUE_MASK": 2963,
"STENCIL_WRITEMASK": 2968,
"STENCIL_BACK_FUNC": 34816,
"STENCIL_BACK_FAIL": 34817,
"STENCIL_BACK_PASS_DEPTH_FAIL": 34818,
"STENCIL_BACK_PASS_DEPTH_PASS": 34819,
"STENCIL_BACK_REF": 36003,
"STENCIL_BACK_VALUE_MASK": 36004,
"STENCIL_BACK_WRITEMASK": 36005,
"VIEWPORT": 2978,
"SCISSOR_BOX": 3088,
"COLOR_CLEAR_VALUE": 3106,
"COLOR_WRITEMASK": 3107,
"UNPACK_ALIGNMENT": 3317,
"PACK_ALIGNMENT": 3333,
"MAX_TEXTURE_SIZE": 3379,
"MAX_VIEWPORT_DIMS": 3386,
"SUBPIXEL_BITS": 3408,
"RED_BITS": 3410,
"GREEN_BITS": 3411,
"BLUE_BITS": 3412,
"ALPHA_BITS": 3413,
"DEPTH_BITS": 3414,
"STENCIL_BITS": 3415,
"POLYGON_OFFSET_UNITS": 10752,
"POLYGON_OFFSET_FACTOR": 32824,
"TEXTURE_BINDING_2D": 32873,
"SAMPLE_BUFFERS": 32936,
"SAMPLES": 32937,
"SAMPLE_COVERAGE_VALUE": 32938,
"SAMPLE_COVERAGE_INVERT": 32939,
"COMPRESSED_TEXTURE_FORMATS": 34467,
"DONT_CARE": 4352,
"FASTEST": 4353,
"NICEST": 4354,
"GENERATE_MIPMAP_HINT": 33170,
"BYTE": 5120,
"UNSIGNED_BYTE": 5121,
"SHORT": 5122,
"UNSIGNED_SHORT": 5123,
"INT": 5124,
"UNSIGNED_INT": 5125,
"FLOAT": 5126,
"DEPTH_COMPONENT": 6402,
"ALPHA": 6406,
"RGB": 6407,
"RGBA": 6408,
"LUMINANCE": 6409,
"LUMINANCE_ALPHA": 6410,
"UNSIGNED_SHORT_4_4_4_4": 32819,
"UNSIGNED_SHORT_5_5_5_1": 32820,
"UNSIGNED_SHORT_5_6_5": 33635,
"FRAGMENT_SHADER": 35632,
"VERTEX_SHADER": 35633,
"MAX_VERTEX_ATTRIBS": 34921,
"MAX_VERTEX_UNIFORM_VECTORS": 36347,
"MAX_VARYING_VECTORS": 36348,
"MAX_COMBINED_TEXTURE_IMAGE_UNITS": 35661,
"MAX_VERTEX_TEXTURE_IMAGE_UNITS": 35660,
"MAX_TEXTURE_IMAGE_UNITS": 34930,
"MAX_FRAGMENT_UNIFORM_VECTORS": 36349,
"SHADER_TYPE": 35663,
"DELETE_STATUS": 35712,
"LINK_STATUS": 35714,
"VALIDATE_STATUS": 35715,
"ATTACHED_SHADERS": 35717,
"ACTIVE_UNIFORMS": 35718,
"ACTIVE_ATTRIBUTES": 35721,
"SHADING_LANGUAGE_VERSION": 35724,
"CURRENT_PROGRAM": 35725,
"NEVER": 512,
"LESS": 513,
"EQUAL": 514,
"LEQUAL": 515,
"GREATER": 516,
"NOTEQUAL": 517,
"GEQUAL": 518,
"ALWAYS": 519,
"KEEP": 7680,
"REPLACE": 7681,
"INCR": 7682,
"DECR": 7683,
"INVERT": 5386,
"INCR_WRAP": 34055,
"DECR_WRAP": 34056,
"VENDOR": 7936,
"RENDERER": 7937,
"VERSION": 7938,
"NEAREST": 9728,
"LINEAR": 9729,
"NEAREST_MIPMAP_NEAREST": 9984,
"LINEAR_MIPMAP_NEAREST": 9985,
"NEAREST_MIPMAP_LINEAR": 9986,
"LINEAR_MIPMAP_LINEAR": 9987,
"TEXTURE_MAG_FILTER": 10240,
"TEXTURE_MIN_FILTER": 10241,
"TEXTURE_WRAP_S": 10242,
"TEXTURE_WRAP_T": 10243,
"TEXTURE": 5890,
"TEXTURE_CUBE_MAP": 34067,
"TEXTURE_BINDING_CUBE_MAP": 34068,
"TEXTURE_CUBE_MAP_POSITIVE_X": 34069,
"TEXTURE_CUBE_MAP_NEGATIVE_X": 34070,
"TEXTURE_CUBE_MAP_POSITIVE_Y": 34071,
"TEXTURE_CUBE_MAP_NEGATIVE_Y": 34072,
"TEXTURE_CUBE_MAP_POSITIVE_Z": 34073,
"TEXTURE_CUBE_MAP_NEGATIVE_Z": 34074,
"MAX_CUBE_MAP_TEXTURE_SIZE": 34076,
"TEXTURE0": 33984,
"TEXTURE1": 33985,
"TEXTURE2": 33986,
"TEXTURE3": 33987,
"TEXTURE4": 33988,
"TEXTURE5": 33989,
"TEXTURE6": 33990,
"TEXTURE7": 33991,
"TEXTURE8": 33992,
"TEXTURE9": 33993,
"TEXTURE10": 33994,
"TEXTURE11": 33995,
"TEXTURE12": 33996,
"TEXTURE13": 33997,
"TEXTURE14": 33998,
"TEXTURE15": 33999,
"TEXTURE16": 34000,
"TEXTURE17": 34001,
"TEXTURE18": 34002,
"TEXTURE19": 34003,
"TEXTURE20": 34004,
"TEXTURE21": 34005,
"TEXTURE22": 34006,
"TEXTURE23": 34007,
"TEXTURE24": 34008,
"TEXTURE25": 34009,
"TEXTURE26": 34010,
"TEXTURE27": 34011,
"TEXTURE28": 34012,
"TEXTURE29": 34013,
"TEXTURE30": 34014,
"TEXTURE31": 34015,
"ACTIVE_TEXTURE": 34016,
"REPEAT": 10497,
"CLAMP_TO_EDGE": 33071,
"MIRRORED_REPEAT": 33648,
"FLOAT_VEC2": 35664,
"FLOAT_VEC3": 35665,
"FLOAT_VEC4": 35666,
"INT_VEC2": 35667,
"INT_VEC3": 35668,
"INT_VEC4": 35669,
"BOOL": 35670,
"BOOL_VEC2": 35671,
"BOOL_VEC3": 35672,
"BOOL_VEC4": 35673,
"FLOAT_MAT2": 35674,
"FLOAT_MAT3": 35675,
"FLOAT_MAT4": 35676,
"SAMPLER_2D": 35678,
"SAMPLER_CUBE": 35680,
"VERTEX_ATTRIB_ARRAY_ENABLED": 34338,
"VERTEX_ATTRIB_ARRAY_SIZE": 34339,
"VERTEX_ATTRIB_ARRAY_STRIDE": 34340,
"VERTEX_ATTRIB_ARRAY_TYPE": 34341,
"VERTEX_ATTRIB_ARRAY_NORMALIZED": 34922,
"VERTEX_ATTRIB_ARRAY_POINTER": 34373,
"VERTEX_ATTRIB_ARRAY_BUFFER_BINDING": 34975,
"IMPLEMENTATION_COLOR_READ_TYPE": 35738,
"IMPLEMENTATION_COLOR_READ_FORMAT": 35739,
"COMPILE_STATUS": 35713,
"LOW_FLOAT": 36336,
"MEDIUM_FLOAT": 36337,
"HIGH_FLOAT": 36338,
"LOW_INT": 36339,
"MEDIUM_INT": 36340,
"HIGH_INT": 36341,
"FRAMEBUFFER": 36160,
"RENDERBUFFER": 36161,
"RGBA4": 32854,
"RGB5_A1": 32855,
"RGB565": 36194,
"DEPTH_COMPONENT16": 33189,
"STENCIL_INDEX8": 36168,
"DEPTH_STENCIL": 34041,
"RENDERBUFFER_WIDTH": 36162,
"RENDERBUFFER_HEIGHT": 36163,
"RENDERBUFFER_INTERNAL_FORMAT": 36164,
"RENDERBUFFER_RED_SIZE": 36176,
"RENDERBUFFER_GREEN_SIZE": 36177,
"RENDERBUFFER_BLUE_SIZE": 36178,
"RENDERBUFFER_ALPHA_SIZE": 36179,
"RENDERBUFFER_DEPTH_SIZE": 36180,
"RENDERBUFFER_STENCIL_SIZE": 36181,
"FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE": 36048,
"FRAMEBUFFER_ATTACHMENT_OBJECT_NAME": 36049,
"FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL": 36050,
"FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE": 36051,
"COLOR_ATTACHMENT0": 36064,
"DEPTH_ATTACHMENT": 36096,
"STENCIL_ATTACHMENT": 36128,
"DEPTH_STENCIL_ATTACHMENT": 33306,
"NONE": 0,
"FRAMEBUFFER_COMPLETE": 36053,
"FRAMEBUFFER_INCOMPLETE_ATTACHMENT": 36054,
"FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT": 36055,
"FRAMEBUFFER_INCOMPLETE_DIMENSIONS": 36057,
"FRAMEBUFFER_UNSUPPORTED": 36061,
"FRAMEBUFFER_BINDING": 36006,
"RENDERBUFFER_BINDING": 36007,
"MAX_RENDERBUFFER_SIZE": 34024,
"INVALID_FRAMEBUFFER_OPERATION": 1286,
"UNPACK_FLIP_Y_WEBGL": 37440,
"UNPACK_PREMULTIPLY_ALPHA_WEBGL": 37441,
"CONTEXT_LOST_WEBGL": 37442,
"UNPACK_COLORSPACE_CONVERSION_WEBGL": 37443,
"BROWSER_DEFAULT_WEBGL": 37444
};

View File

@ -0,0 +1,142 @@
let i = 1;
const GLmethod = {};
GLmethod.activeTexture = i++; //1
GLmethod.attachShader = i++;
GLmethod.bindAttribLocation = i++;
GLmethod.bindBuffer = i++;
GLmethod.bindFramebuffer = i++;
GLmethod.bindRenderbuffer = i++;
GLmethod.bindTexture = i++;
GLmethod.blendColor = i++;
GLmethod.blendEquation = i++;
GLmethod.blendEquationSeparate = i++; //10
GLmethod.blendFunc = i++;
GLmethod.blendFuncSeparate = i++;
GLmethod.bufferData = i++;
GLmethod.bufferSubData = i++;
GLmethod.checkFramebufferStatus = i++;
GLmethod.clear = i++;
GLmethod.clearColor = i++;
GLmethod.clearDepth = i++;
GLmethod.clearStencil = i++;
GLmethod.colorMask = i++; //20
GLmethod.compileShader = i++;
GLmethod.compressedTexImage2D = i++;
GLmethod.compressedTexSubImage2D = i++;
GLmethod.copyTexImage2D = i++;
GLmethod.copyTexSubImage2D = i++;
GLmethod.createBuffer = i++;
GLmethod.createFramebuffer = i++;
GLmethod.createProgram = i++;
GLmethod.createRenderbuffer = i++;
GLmethod.createShader = i++; //30
GLmethod.createTexture = i++;
GLmethod.cullFace = i++;
GLmethod.deleteBuffer = i++;
GLmethod.deleteFramebuffer = i++;
GLmethod.deleteProgram = i++;
GLmethod.deleteRenderbuffer = i++;
GLmethod.deleteShader = i++;
GLmethod.deleteTexture = i++;
GLmethod.depthFunc = i++;
GLmethod.depthMask = i++; //40
GLmethod.depthRange = i++;
GLmethod.detachShader = i++;
GLmethod.disable = i++;
GLmethod.disableVertexAttribArray = i++;
GLmethod.drawArrays = i++;
GLmethod.drawArraysInstancedANGLE = i++;
GLmethod.drawElements = i++;
GLmethod.drawElementsInstancedANGLE = i++;
GLmethod.enable = i++;
GLmethod.enableVertexAttribArray = i++; //50
GLmethod.flush = i++;
GLmethod.framebufferRenderbuffer = i++;
GLmethod.framebufferTexture2D = i++;
GLmethod.frontFace = i++;
GLmethod.generateMipmap = i++;
GLmethod.getActiveAttrib = i++;
GLmethod.getActiveUniform = i++;
GLmethod.getAttachedShaders = i++;
GLmethod.getAttribLocation = i++;
GLmethod.getBufferParameter = i++; //60
GLmethod.getContextAttributes = i++;
GLmethod.getError = i++;
GLmethod.getExtension = i++;
GLmethod.getFramebufferAttachmentParameter = i++;
GLmethod.getParameter = i++;
GLmethod.getProgramInfoLog = i++;
GLmethod.getProgramParameter = i++;
GLmethod.getRenderbufferParameter = i++;
GLmethod.getShaderInfoLog = i++;
GLmethod.getShaderParameter = i++; //70
GLmethod.getShaderPrecisionFormat = i++;
GLmethod.getShaderSource = i++;
GLmethod.getSupportedExtensions = i++;
GLmethod.getTexParameter = i++;
GLmethod.getUniform = i++;
GLmethod.getUniformLocation = i++;
GLmethod.getVertexAttrib = i++;
GLmethod.getVertexAttribOffset = i++;
GLmethod.isBuffer = i++;
GLmethod.isContextLost = i++; //80
GLmethod.isEnabled = i++;
GLmethod.isFramebuffer = i++;
GLmethod.isProgram = i++;
GLmethod.isRenderbuffer = i++;
GLmethod.isShader = i++;
GLmethod.isTexture = i++;
GLmethod.lineWidth = i++;
GLmethod.linkProgram = i++;
GLmethod.pixelStorei = i++;
GLmethod.polygonOffset = i++; //90
GLmethod.readPixels = i++;
GLmethod.renderbufferStorage = i++;
GLmethod.sampleCoverage = i++;
GLmethod.scissor = i++;
GLmethod.shaderSource = i++;
GLmethod.stencilFunc = i++;
GLmethod.stencilFuncSeparate = i++;
GLmethod.stencilMask = i++;
GLmethod.stencilMaskSeparate = i++;
GLmethod.stencilOp = i++; //100
GLmethod.stencilOpSeparate = i++;
GLmethod.texImage2D = i++;
GLmethod.texParameterf = i++;
GLmethod.texParameteri = i++;
GLmethod.texSubImage2D = i++;
GLmethod.uniform1f = i++;
GLmethod.uniform1fv = i++;
GLmethod.uniform1i = i++;
GLmethod.uniform1iv = i++;
GLmethod.uniform2f = i++; //110
GLmethod.uniform2fv = i++;
GLmethod.uniform2i = i++;
GLmethod.uniform2iv = i++;
GLmethod.uniform3f = i++;
GLmethod.uniform3fv = i++;
GLmethod.uniform3i = i++;
GLmethod.uniform3iv = i++;
GLmethod.uniform4f = i++;
GLmethod.uniform4fv = i++;
GLmethod.uniform4i = i++; //120
GLmethod.uniform4iv = i++;
GLmethod.uniformMatrix2fv = i++;
GLmethod.uniformMatrix3fv = i++;
GLmethod.uniformMatrix4fv = i++;
GLmethod.useProgram = i++;
GLmethod.validateProgram = i++;
GLmethod.vertexAttrib1f = i++; //new
GLmethod.vertexAttrib2f = i++; //new
GLmethod.vertexAttrib3f = i++; //new
GLmethod.vertexAttrib4f = i++; //new //130
GLmethod.vertexAttrib1fv = i++; //new
GLmethod.vertexAttrib2fv = i++; //new
GLmethod.vertexAttrib3fv = i++; //new
GLmethod.vertexAttrib4fv = i++; //new
GLmethod.vertexAttribPointer = i++;
GLmethod.viewport = i++;
export default GLmethod;

View File

@ -0,0 +1,23 @@
const GLtype = {};
[
"GLbitfield",
"GLboolean",
"GLbyte",
"GLclampf",
"GLenum",
"GLfloat",
"GLint",
"GLintptr",
"GLsizei",
"GLsizeiptr",
"GLshort",
"GLubyte",
"GLuint",
"GLushort"
].sort().map((typeName, i) => GLtype[typeName] = 1 >> (i + 1));
export default GLtype;

View File

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLProgram';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLProgram {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

View File

@ -0,0 +1,21 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLRenderBuffer';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLRenderbuffer {
className = name;
constructor(id) {
this.id = id;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLShader';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLShader {
className = name;
constructor(id, type) {
this.id = id;
this.type = type;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

View File

@ -0,0 +1,11 @@
export default class WebGLShaderPrecisionFormat {
className = 'WebGLShaderPrecisionFormat';
constructor({
rangeMin, rangeMax, precision
}) {
this.rangeMin = rangeMin;
this.rangeMax = rangeMax;
this.precision = precision;
}
}

View File

@ -0,0 +1,22 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLTexture';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLTexture {
className = name;
constructor(id, type) {
this.id = id;
this.type = type;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

View File

@ -0,0 +1,22 @@
import {getTransferedObjectUUID} from './classUtils';
const name = 'WebGLUniformLocation';
function uuid(id) {
return getTransferedObjectUUID(name, id);
}
export default class WebGLUniformLocation {
className = name;
constructor(id, type) {
this.id = id;
this.type = type;
}
static uuid = uuid;
uuid() {
return uuid(this.id);
}
}

View File

@ -0,0 +1,3 @@
export function getTransferedObjectUUID(name, id) {
return `${name.toLowerCase()}-${id}`;
}

View File

@ -0,0 +1,74 @@
import GContext2D from '../context-2d/RenderingContext';
import GContextWebGL from '../context-webgl/RenderingContext';
export default class GCanvas {
// static GBridge = null;
id = null;
_needRender = true;
constructor(id, { disableAutoSwap }) {
this.id = id;
this._disableAutoSwap = disableAutoSwap;
if (disableAutoSwap) {
this._swapBuffers = () => {
GCanvas.GBridge.render(this.id);
}
}
}
getContext(type) {
let context = null;
if (type.match(/webgl/i)) {
context = new GContextWebGL(this);
context.componentId = this.id;
if (!this._disableAutoSwap) {
const render = () => {
if (this._needRender) {
GCanvas.GBridge.render(this.id);
this._needRender = false;
}
}
setInterval(render, 16);
}
GCanvas.GBridge.callSetContextType(this.id, 1); // 0 for 2d; 1 for webgl
} else if (type.match(/2d/i)) {
context = new GContext2D(this);
context.componentId = this.id;
// const render = ( callback ) => {
//
// const commands = context._drawCommands;
// context._drawCommands = '';
//
// GCanvas.GBridge.render2d(this.id, commands, callback);
// this._needRender = false;
// }
// //draw方法触发
// context._flush = render;
// //setInterval(render, 16);
GCanvas.GBridge.callSetContextType(this.id, 0);
} else {
throw new Error('not supported context ' + type);
}
return context;
}
reset() {
GCanvas.GBridge.callReset(this.id);
}
}

View File

@ -0,0 +1,96 @@
let incId = 1;
const noop = function () { };
class GImage {
static GBridge = null;
constructor() {
this._id = incId++;
this._width = 0;
this._height = 0;
this._src = undefined;
this._onload = noop;
this._onerror = noop;
this.complete = false;
}
get width() {
return this._width;
}
set width(v) {
this._width = v;
}
get height() {
return this._height;
}
set height(v) {
this._height = v;
}
get src() {
return this._src;
}
set src(v) {
if (v.startsWith('//')) {
v = 'http:' + v;
}
this._src = v;
GImage.GBridge.perloadImage([this._src, this._id], (data) => {
if (typeof data === 'string') {
data = JSON.parse(data);
}
if (data.error) {
var evt = { type: 'error', target: this };
this.onerror(evt);
} else {
this.complete = true;
this.width = typeof data.width === 'number' ? data.width : 0;
this.height = typeof data.height === 'number' ? data.height : 0;
var evt = { type: 'load', target: this };
this.onload(evt);
}
});
}
addEventListener(name, listener) {
if (name === 'load') {
this.onload = listener;
} else if (name === 'error') {
this.onerror = listener;
}
}
removeEventListener(name, listener) {
if (name === 'load') {
this.onload = noop;
} else if (name === 'error') {
this.onerror = noop;
}
}
get onload() {
return this._onload;
}
set onload(v) {
this._onload = v;
}
get onerror() {
return this._onerror;
}
set onerror(v) {
this._onerror = v;
}
}
export default GImage;

View File

@ -0,0 +1,24 @@
export function ArrayBufferToBase64 (buffer) {
var binary = '';
var bytes = new Uint8ClampedArray(buffer);
for (var len = bytes.byteLength, i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
export function Base64ToUint8ClampedArray(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = atob(base64);
const outputArray = new Uint8ClampedArray(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}

View File

@ -0,0 +1,39 @@
import GCanvas from './env/canvas';
import GImage from './env/image';
import GWebGLRenderingContext from './context-webgl/RenderingContext';
import GContext2D from './context-2d/RenderingContext';
import GBridgeWeex from './bridge/bridge-weex';
export let Image = GImage;
export let WeexBridge = GBridgeWeex;
export function enable(el, { bridge, debug, disableAutoSwap, disableComboCommands } = {}) {
const GBridge = GImage.GBridge = GCanvas.GBridge = GWebGLRenderingContext.GBridge = GContext2D.GBridge = bridge;
GBridge.callEnable(el.ref, [
0, // renderMode: 0--RENDERMODE_WHEN_DIRTY, 1--RENDERMODE_CONTINUOUSLY
-1, // hybridLayerType: 0--LAYER_TYPE_NONE 1--LAYER_TYPE_SOFTWARE 2--LAYER_TYPE_HARDWARE
false, // supportScroll
false, // newCanvasMode
1, // compatible
'white',// clearColor
false // sameLevel: newCanvasMode = true && true => GCanvasView and Webview is same level
]);
if (debug === true) {
GBridge.callEnableDebug();
}
if (disableComboCommands) {
GBridge.callEnableDisableCombo();
}
var canvas = new GCanvas(el.ref, { disableAutoSwap });
canvas.width = el.style.width;
canvas.height = el.style.height;
return canvas;
};

View File

@ -0,0 +1,12 @@
{
"name": "uqrcode",
"version": "3.5.1",
"description": "",
"main": "uqrcode.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,80 @@
{
"id": "Sansnn-uQRCode",
"displayName": "uQRCode 全端二维码生成插件 支持nvue 支持nodejs服务端",
"version": "4.0.6",
"description": "uQRCode是一款基于Javascript环境开发的二维码生成插件适用所有Javascript运行环境的前端应用和Node.js。",
"keywords": [
"二维码",
"uQRCode",
"qrcode",
"qr"
],
"repository": "https://github.com/Sansnn/uQRCode",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/uqrcodejs",
"type": "sdk-js"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,392 @@
# 介绍
`uQRCode`是一款基于`Javascript`环境开发的二维码生成插件,适用所有`Javascript`运行环境的前端应用和`Node.js`应用。
`uQRCode`可扩展性高,它支持自定义渲染二维码,可通过`uQRCode API`得到二维码绘制关键信息后,使用`canvas``svg``js`操作`dom`的方式绘制二维码图案。还可自定义二维码样式,如随机颜色、圆点、方块、块与块之间的间距等。
欢迎加入群聊【uQRCode交流群】[695070434](https://jq.qq.com/?_wv=1027&k=JRjzDqiw)。
# 设计器
uQRCode发布了配套的可视化设计器可根据自己喜好在设计器中设计二维码样式一键生成配置代码复制到项目中详情请在微信小程序搜索“柚子二维码”或扫描下方小程序码体验。
![uQRCode设计器](https://uqrcode.cn/mp_weixin_code.jpg)
## 设计器模板示例
![uQRCode设计器](https://uqrcode.cn/yz_1.png)
![uQRCode设计器](https://uqrcode.cn/yz_2.png)
![uQRCode设计器](https://uqrcode.cn/yz_3.png)
![uQRCode设计器](https://uqrcode.cn/yz_4.png)
![uQRCode设计器](https://uqrcode.cn/yz_5.png)
![uQRCode设计器](https://uqrcode.cn/yz_6.png)
![uQRCode设计器](https://uqrcode.cn/yz_7.png)
![uQRCode设计器](https://uqrcode.cn/yz_8.png)
![uQRCode设计器](https://uqrcode.cn/yz_9.png)
# 快速上手
> 在`uni-app`中,我们更推荐使用组件方式来生成二维码,组件方式大大提高了页面的可读性以及避开了一些平台容易出问题的地方,当组件无法满足需求的时候,再考虑切换成原生方式。
官方文档:[https://uqrcode.cn/doc](https://uqrcode.cn/doc)。
github地址[https://github.com/Sansnn/uQRCode](https://github.com/Sansnn/uQRCode)。
npm地址[https://www.npmjs.com/package/uqrcodejs](https://www.npmjs.com/package/uqrcodejs)。
uni-app插件市场地址[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287)。
## 原生方式
原生方式仅需要获取`uqrcode.js`文件便可使用。详细配置请移步到:文档 > [原生](https://uqrcode.cn/doc/document/native.html)。
### 安装
1. 通过`npm`安装,成功后即可使用`import``require`进行引用。
``` bash
# npm安装
npm install uqrcodejs
# 或者
npm install @uqrcode/js
```
2. 通过项目开源地址获取`uqrcode.js`,下载`uqrcode.js`后,将其复制到您项目指定目录,在页面中引入`uqrcode.js`文件即可开始使用。
### 引入
- 通过`import`引入。
``` javascript
// npm安装
import UQRCode from 'uqrcodejs'; // npm install uqrcodejs
// 或者
import UQRCode from '@uqrcode/js'; // npm install @uqrcode/js
```
- `Node.js`通过`require`引入。
``` javascript
// npm安装
const UQRCode = require('uqrcodejs'); // npm install uqrcodejs
// 或者
const UQRCode = require('@uqrcode/js'); // npm install @uqrcode/js
```
- 原生浏览器环境在js脚本加载时添加到`window`。
``` html
<script type="text/javascript" src="uqrcode.js"></script>
<script>
var UQRCode = window.UQRCode;
</script>
```
### 简单用法
`uQRCode`基于`Canvas API`封装了一套方法,建议开发者使用`canvas`生成,一键调用,非常方便。以下是示例:
- HTML示例
- DOM部分
``` html
<canvas id="qrcode" width="200" height="200"></canvas>
```
- JS部分
``` javascript
// 获取uQRCode实例
var qr = new UQRCode();
// 设置二维码内容
qr.data = "https://uqrcode.cn/doc";
// 设置二维码大小必须与canvas设置的宽高一致
qr.size = 200;
// 调用制作二维码方法
qr.make();
// 获取canvas元素
var canvas = document.getElementById("qrcode");
// 获取canvas上下文
var canvasContext = canvas.getContext("2d");
// 设置uQRCode实例的canvas上下文
qr.canvasContext = canvasContext;
// 调用绘制方法将二维码图案绘制到canvas上
qr.drawCanvas();
```
- uni-app示例
- Template部分
``` html
<canvas id="qrcode" canvas-id="qrcode" style="width: 200px;height: 200px;"></canvas>
```
- JS部分
``` javascript
onReady() {
// 获取uQRCode实例
var qr = new UQRCode();
// 设置二维码内容
qr.data = "https://uqrcode.cn/doc";
// 设置二维码大小必须与canvas设置的宽高一致
qr.size = 200;
// 调用制作二维码方法
qr.make();
// 获取canvas上下文
var canvasContext = uni.createCanvasContext('qrcode', this); // 如果是组件this必须传入
// 设置uQRCode实例的canvas上下文
qr.canvasContext = canvasContext;
// 调用绘制方法将二维码图案绘制到canvas上
qr.drawCanvas();
}
```
- 微信小程序推荐使用Canvas 2D关于Canvas 2D的使用请参考微信开放文档。
### 高级用法
考虑到部分平台可能不支持`canvas`,所以`uQRCode`并没有强制要求和`canvas`一起使用,您还可以选择其他方式来生成二维码,例如使用`js`操作`dom`进行绘制或是使用`svg`绘制等。以下是示例:
- uni-app v-for+view
```html
<template>
<view>
<view class="qrcode">
<view v-for="(row, rowI) in modules" :key="rowI" style="display: flex;flex-direction: row;">
<view v-for="(col, colI) in row" :key="colI">
<view v-if="col.isBlack" style="width: 10px;height: 10px;background-color: black;">
<!-- 黑色码点 -->
</view>
<view v-else style="width: 10px;height: 10px;background-color: white;">
<!-- 白色码点 -->
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import UQRCode from '../../uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js';
export default {
data() {
return {
modules: []
}
},
onLoad() {
const qr = new UQRCode();
qr.data = 'uQRCode';
qr.make();
this.modules = qr.modules;
},
methods: {
}
}
</script>
```
- js操作dom
``` html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>uQRCode二维码生成</title>
</head>
<body>
<div id="qrcode" style="position: relative;"></div>
<script type="text/javascript" src="uqrcode.js"></script>
<script>
// 引入uQRCode
var UQRCode = window.UQRCode;
// 获取uQRCode实例
var qr = new UQRCode();
// 设置二维码内容
qr.data = "https://uqrcode.cn/doc";
// 设置二维码大小必须与canvas设置的宽高一致
qr.size = 200;
// 设置二维码前景图,可以是路径
qr.foregroundImageSrc =
'data:image/png;base64,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';
// 调用制作二维码方法
qr.make();
var drawModules = qr.getDrawModules();
// 遍历drawModules创建dom元素
var qrHtml = '';
for (var i = 0; i < drawModules.length; i++) {
var drawModule = drawModules[i];
switch (drawModule.type) {
case 'tile':
/* 绘制小块 */
qrHtml += `<div style="position: absolute;left: ${drawModule.x}px;top: ${drawModule.y}px;width: ${drawModule.width}px;height: ${drawModule.height}px;background: ${drawModule.color};"></div>`;
break;
case 'image':
/* 绘制图像 */
qrHtml += `<img style="position: absolute;left: ${drawModule.x}px;top: ${drawModule.y}px;width: ${drawModule.width}px;height: ${drawModule.height}px;" src="${drawModule.imageSrc}" />`;
break;
}
}
document.getElementById('qrcode').innerHTML = qrHtml;
</script>
</body>
</html>
```
- svg
``` html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>uQRCode二维码生成</title>
</head>
<body>
<svg id="qrcode" width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1"></svg>
<script type="text/javascript" src="uqrcode.js"></script>
<script>
// 引入uQRCode
var UQRCode = window.UQRCode;
// 获取uQRCode实例
var qr = new UQRCode();
// 设置二维码内容
qr.data = "https://uqrcode.cn/doc";
// 设置二维码大小必须与canvas设置的宽高一致
qr.size = 200;
// 设置二维码前景图,可以是路径
qr.foregroundImageSrc =
'data:image/png;base64,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';
// 调用制作二维码方法
qr.make();
var drawModules = qr.getDrawModules();
// 遍历drawModules创建svg元素
var qrHtml = '';
for (var i = 0; i < drawModules.length; i++) {
var drawModule = drawModules[i];
switch (drawModule.type) {
case 'tile':
/* 绘制小块 */
qrHtml += `<rect x="${drawModule.x}" y="${drawModule.y}" width="${drawModule.width}" height="${drawModule.height}" style="fill: ${drawModule.color};" />`;
break;
case 'image':
/* 绘制图像 */
qrHtml += `<image href="${drawModule.imageSrc}" x="${drawModule.x}" y="${drawModule.y}" width="${drawModule.width}" height="${drawModule.height}" />`;
break;
}
}
document.getElementById('qrcode').innerHTML = qrHtml;
</script>
</body>
</html>
```
> 更多用法大家自行探索咯,期待分享哟~
### 导出临时文件路径
原生方式基于`Canvas`的,请自行参阅各平台`Canvas`的导出方式。以下是部分示例:
- uni-app
```javascript
// 通过uni.createCanvasContext方式创建绘制上下文的对应导出API为uni.canvasToTempFilePath
// 调用完ctx.draw()方法后不能第一时间导出,否则会异常,需要有一定的延时
setTimeout(() => {
uni.canvasToTempFilePath(
{
canvasId: this.canvasId,
fileType: this.fileType,
width: this.canvasWidth,
height: this.canvasHeight,
success: res => {
console.log(res);
},
fail: err => {
console.log(err);
}
},
// this // 组件内使用必传当前实例
);
}, 300);
```
- Canvas2D
```javascript
// 得到base64
console.log(canvas.toDataURL());
// 得到buffer
console.log(canvas.toBuffer());
```
### 保存二维码到本地相册
必须在导出临时文件路径成功后再执行保存。uni-app通用保存方式H5除外
```javascript
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: res => {
console.log(res);
},
fail: err => {
console.log(err);
}
});
```
H5可以通过设置`<a>`标签`href`属性的方式进行保存:
```javascript
const aEle = document.createElement('a');
aEle.download = 'uQRCode'; // 设置下载的文件名,默认是'下载'
aEle.href = tempFilePath;
document.body.appendChild(aEle);
aEle.click();
aEle.remove(); // 下载之后把创建的元素删除
```
经过测试PC端浏览器可以下载部分安卓自带或第三方浏览器可以下载安卓微信浏览器不适用移动端iOS所有浏览器均不适用差异较大还是推荐各位导出文件给图片组件显示然后提示用户通过长按图片进行保存这种方式。
## uni-app组件方式
### 安装
通过uni-app插件市场地址安装[https://ext.dcloud.net.cn/plugin?id=1287](https://ext.dcloud.net.cn/plugin?id=1287)。详细配置请移步到:文档 > [uni-app组件](https://uqrcode.cn/doc/document/uni-app.html)。
### 引入
uni-app默认为easycom模式可直接键入`<uqrcode>`标签。
### 简单用法
安装`uqrcode`组件后,在`template`中键入`<uqrcode/>`。设置`ref`属性可使用组件内部方法,`canvas-id`属性为组件内部的canvas组件标识`value`属性为二维码生成对应内容,`options`为配置选项可配置二维码样式绘制Logo等详见[options](https://uqrcode.cn/doc/document/uni-app.html#options) 。
``` html
<uqrcode ref="uqrcode" canvas-id="qrcode" value="https://uqrcode.cn/doc" :options="{ margin: 10 }"></uqrcode>
```
### 导出临时文件路径
为了保证方法调用成功,请在 [complete](https://uqrcode.cn/doc/document/uni-app.html#complete) 事件返回`success=true`后调用。
```javascript
// uqrcode为组件的ref名称
this.$refs.uqrcode.toTempFilePath({
success: res => {
console.log(res);
}
});
```
### 保存二维码到本地相册
为了保证方法调用成功,请在 [complete](https://uqrcode.cn/doc/document/uni-app.html#complete) 事件返回`success=true`后调用。
```javascript
// uqrcode为组件的ref名称
this.$refs.uqrcode.save({
success: () => {
uni.showToast({
icon: 'success',
title: '保存成功'
});
}
});
```
## 更多使用说明请前往官方文档查看:[https://uqrcode.cn/doc](https://uqrcode.cn/doc)。

View File

@ -0,0 +1,2 @@
## 1.0.02021-07-12
第一版发布

View File

@ -0,0 +1,132 @@
// #ifdef APP-PLUS
import permision from "./wa-permission/permission.js"
// #endif
class Gps {
constructor(arg) {
this.lock = false //锁防止重复请求
}
async getLocation(param = {
type: 'wgs84'
}) {
return new Promise(async (callback) => {
if (this.lock) {
// console.log('已锁,已有另一个请求正在执行。无需重复请求');
callback(false);
return false
}
this.lock = true //加锁防止重复的请求
uni.getLocation({
...param,
success: res => {
this.lock = false //成功后解锁
//console.log(res);
callback(res)
},
fail: async (err) => {
uni.showToast({
title: '定位获取失败',
icon: 'none'
});
console.error(err)
callback(false)
// #ifdef APP-PLUS
await this.checkGpsIsOpen()
// #endif
// #ifdef MP-WEIXIN
if (err.errMsg == 'getLocation:fail auth deny') {
uni.showModal({
content: '应用无定位权限',
confirmText: '前往设置',
complete: (e) => {
if (e.confirm) {
uni.openSetting({
success(res) {
console.log(res.authSetting)
}
});
}
this.lock = false //解锁让回到界面重新获取
}
});
}
if (err.errMsg == 'getLocation:fail:ERROR_NOCELL&WIFI_LOCATIONSWITCHOFF') {
uni.showModal({
content: '未开启定位权限,请前往手机系统设置中开启',
showCancel: false,
confirmText:"知道了"
});
}
// #endif
}
});
})
}
// #ifdef APP-PLUS
async checkGpsIsOpen() {
this.lock = true //加锁防止重复的请求
// console.log('检查定位设置开启问题', permision.checkSystemEnableLocation());
if (!permision.checkSystemEnableLocation()) {
plus.nativeUI.confirm("手机定位权限没有开启,是否去开启?", (e) => {
this.lock = false
if (e.index == 0) {
if (uni.getSystemInfoSync().platform == "ios") {
plus.runtime.openURL("app-settings://");
} else {
var main = plus.android.runtimeMainActivity(); //获取activity
var Intent = plus.android.importClass('android.content.Intent');
var Settings = plus.android.importClass('android.provider.Settings');
var intent = new Intent(Settings
.ACTION_LOCATION_SOURCE_SETTINGS); //可设置表中所有Action字段
main.startActivity(intent);
}
} else {
uni.showToast({
title: '设备无定位权限',
icon: 'none'
});
callback(false)
}
}, {
"buttons": ["去设置", "不开启"],
"verticalAlign": "center"
});
return false
}
let checkAppGpsRes = await this.checkAppGps()
// console.log(checkAppGpsRes, 'checkAppGpsRes');
if (!checkAppGpsRes) {
plus.nativeUI.confirm("应用定位权限没有开启,是否去开启?", (e) => {
this.lock = false
if (e.index == 0) {
permision.gotoAppPermissionSetting()
callback(false)
} else {
uni.showToast({
title: '应用无定位权限',
icon: 'none'
});
return false
}
}, {
"buttons": ["去设置", "不开启"],
"verticalAlign": "center"
});
} else {
this.lock = false
}
}
async checkAppGps() {
if (uni.getSystemInfoSync().platform == "ios" && !permision.judgeIosPermission("location")) {
return false
}
if (uni.getSystemInfoSync().platform != "ios" && await permision.requestAndroidPermission(
"android.permission.ACCESS_FINE_LOCATION") != 1) {
return false
}
return true
}
// #endif
}
export default Gps

View File

@ -0,0 +1,272 @@
/**
* 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
*/
var isIos
// #ifdef APP-PLUS
isIos = (plus.os.name == "iOS")
// #endif
// 判断推送权限是否开启
function judgeIosPermissionPush() {
var result = false;
var UIApplication = plus.ios.import("UIApplication");
var app = UIApplication.sharedApplication();
var enabledTypes = 0;
if (app.currentUserNotificationSettings) {
var settings = app.currentUserNotificationSettings();
enabledTypes = settings.plusGetAttribute("types");
console.log("enabledTypes1:" + enabledTypes);
if (enabledTypes == 0) {
console.log("推送权限没有开启");
} else {
result = true;
console.log("已经开启推送功能!")
}
plus.ios.deleteObject(settings);
} else {
enabledTypes = app.enabledRemoteNotificationTypes();
if (enabledTypes == 0) {
console.log("推送权限没有开启!");
} else {
result = true;
console.log("已经开启推送功能!")
}
console.log("enabledTypes2:" + enabledTypes);
}
plus.ios.deleteObject(app);
plus.ios.deleteObject(UIApplication);
return result;
}
// 判断定位权限是否开启
function judgeIosPermissionLocation() {
var result = false;
var cllocationManger = plus.ios.import("CLLocationManager");
var status = cllocationManger.authorizationStatus();
result = (status != 2)
// console.log("定位权限开启:" + result);
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
/* var enable = cllocationManger.locationServicesEnabled();
var status = cllocationManger.authorizationStatus();
console.log("enable:" + enable);
console.log("status:" + status);
if (enable && status != 2) {
result = true;
console.log("手机定位服务已开启且已授予定位权限");
} else {
console.log("手机系统的定位没有打开或未给予定位权限");
} */
plus.ios.deleteObject(cllocationManger);
return result;
}
// 判断麦克风权限是否开启
function judgeIosPermissionRecord() {
var result = false;
var avaudiosession = plus.ios.import("AVAudioSession");
var avaudio = avaudiosession.sharedInstance();
var permissionStatus = avaudio.recordPermission();
console.log("permissionStatus:" + permissionStatus);
if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
console.log("麦克风权限没有开启");
} else {
result = true;
console.log("麦克风权限已经开启");
}
plus.ios.deleteObject(avaudiosession);
return result;
}
// 判断相机权限是否开启
function judgeIosPermissionCamera() {
var result = false;
var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
console.log("authStatus:" + authStatus);
if (authStatus == 3) {
result = true;
console.log("相机权限已经开启");
} else {
console.log("相机权限没有开启");
}
plus.ios.deleteObject(AVCaptureDevice);
return result;
}
// 判断相册权限是否开启
function judgeIosPermissionPhotoLibrary() {
var result = false;
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
var authStatus = PHPhotoLibrary.authorizationStatus();
console.log("authStatus:" + authStatus);
if (authStatus == 3) {
result = true;
console.log("相册权限已经开启");
} else {
console.log("相册权限没有开启");
}
plus.ios.deleteObject(PHPhotoLibrary);
return result;
}
// 判断通讯录权限是否开启
function judgeIosPermissionContact() {
var result = false;
var CNContactStore = plus.ios.import("CNContactStore");
var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
if (cnAuthStatus == 3) {
result = true;
console.log("通讯录权限已经开启");
} else {
console.log("通讯录权限没有开启");
}
plus.ios.deleteObject(CNContactStore);
return result;
}
// 判断日历权限是否开启
function judgeIosPermissionCalendar() {
var result = false;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
if (ekAuthStatus == 3) {
result = true;
console.log("日历权限已经开启");
} else {
console.log("日历权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
// 判断备忘录权限是否开启
function judgeIosPermissionMemo() {
var result = false;
var EKEventStore = plus.ios.import("EKEventStore");
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
if (ekAuthStatus == 3) {
result = true;
console.log("备忘录权限已经开启");
} else {
console.log("备忘录权限没有开启");
}
plus.ios.deleteObject(EKEventStore);
return result;
}
// Android权限查询
function requestAndroidPermission(permissionID) {
return new Promise((resolve, reject) => {
plus.android.requestPermissions(
[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
function(resultObj) {
var result = 0;
for (var i = 0; i < resultObj.granted.length; i++) {
var grantedPermission = resultObj.granted[i];
console.log('已获取的权限:' + grantedPermission);
result = 1
}
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
var deniedPresentPermission = resultObj.deniedPresent[i];
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
result = 0
}
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
var deniedAlwaysPermission = resultObj.deniedAlways[i];
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
result = -1
}
resolve(result);
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
// if (result != 1) {
// gotoAppPermissionSetting()
// }
},
function(error) {
console.log('申请权限错误:' + error.code + " = " + error.message);
resolve({
code: error.code,
message: error.message
});
}
);
});
}
// 使用一个方法,根据参数判断权限
function judgeIosPermission(permissionID) {
if (permissionID == "location") {
return judgeIosPermissionLocation()
} else if (permissionID == "camera") {
return judgeIosPermissionCamera()
} else if (permissionID == "photoLibrary") {
return judgeIosPermissionPhotoLibrary()
} else if (permissionID == "record") {
return judgeIosPermissionRecord()
} else if (permissionID == "push") {
return judgeIosPermissionPush()
} else if (permissionID == "contact") {
return judgeIosPermissionContact()
} else if (permissionID == "calendar") {
return judgeIosPermissionCalendar()
} else if (permissionID == "memo") {
return judgeIosPermissionMemo()
}
return false;
}
// 跳转到**应用**的权限页面
function gotoAppPermissionSetting() {
if (isIos) {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
// console.log(plus.device.vendor);
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
// 检查系统的设备服务是否开启
// var checkSystemEnableLocation = async function () {
function checkSystemEnableLocation() {
if (isIos) {
var result = false;
var cllocationManger = plus.ios.import("CLLocationManager");
var result = cllocationManger.locationServicesEnabled();
// console.log("系统定位开启:" + result);
plus.ios.deleteObject(cllocationManger);
return result;
} else {
var context = plus.android.importClass("android.content.Context");
var locationManager = plus.android.importClass("android.location.LocationManager");
var main = plus.android.runtimeMainActivity();
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
console.log("系统定位开启:" + result);
return result
}
}
export default {
judgeIosPermission: judgeIosPermission,
requestAndroidPermission: requestAndroidPermission,
checkSystemEnableLocation: checkSystemEnableLocation,
gotoAppPermissionSetting: gotoAppPermissionSetting
}

View File

@ -0,0 +1,76 @@
{
"id": "json-gps",
"displayName": "地理位置获取方法支持在onShow生命周期使用集成权限判断和引导开启包括设备权限和应用权限",
"version": "1.0.0",
"description": "支持在onShow生命周期使用集成权限判断和引导开启包括设备权限和应用权限的地理位置获取方法",
"keywords": [
"权限引导"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,30 @@
### 插件简介:
支持在onShow生命周期使用的集成权限判断和引导开启包括设备权限和应用权限的地理位置获取方法
### 插件背景:
实现获取用户地理位置,当手机未开启定位模块或应用无定位权限时,引导用户前往手机系统或应用权限设置页面。设置完回到应用界面自动重新获取。
为了实现该效果开发者把获取定位权限放在onShow生命周期然而即使是原生开发调用判断设备权限操作也会触发onShow生命周期直接使用会导致死循环。因此本插件二次封装用锁的方式控制该问题。
### 使用方式
```js
import Gps from '@/uni_modules/json-gps/js_sdk/gps.js';
const gps = new Gps()
export default {
async onShow() {
uni.showLoading({
title:"获取定位中"
});
let location = await gps.getLocation()
console.log(location);
if(location){
uni.showToast({
title: JSON.stringify(location),
icon: 'none'
});
}
uni.hideLoading()
}
}
```
>本插件基于第三方插件:[App权限判断和提示](https://ext.dcloud.net.cn/plugin?id=594)二次封装而成

View File

@ -0,0 +1,6 @@
## 1.0.22021-05-20
修复IOS提示不准确无摄像头权限提示了无法访问相册
## 1.0.12021-05-20
新增文档和示例代码
## 1.0.02021-05-20
第一版本发布

View File

@ -0,0 +1,70 @@
export default function(){
//当应用无访问摄像头/相册权限,引导跳到设置界面
uni.addInterceptor('chooseImage', {
fail(e) { // 失败回调拦截 更多拦截器用法 [详情](https://uniapp.dcloud.io/api/interceptor?id=addinterceptor)
console.log(e);
if (uni.getSystemInfoSync().platform == "android" && e.errMsg == 'chooseImage:fail No Permission') {
if (e.code === 11) {
uni.showModal({
title: "无法访问摄像头",
content: "当前无摄像头访问权限,建议前往设置",
confirmText: "前往设置",
success(e) {
if (e.confirm) {
gotoAppPermissionSetting()
}
}
});
} else {
uni.showModal({
title: "无法访问相册",
content: "当前无系统相册访问权限,建议前往设置",
confirmText: "前往设置",
success(e) {
if (e.confirm) {
gotoAppPermissionSetting()
}
}
});
}
} else if (e.errCode === 2&&e.errMsg == "chooseImage:fail No filming permission") {
console.log('e.errMsg === 2 ios无法拍照权限 ');
// 注e.errCode === 8 ios无从相册选择图片的权限 api已内置无需自己用拦截器实现
uni.showModal({
title: "无法访问摄像头",
content: "当前无摄像头访问权限,建议前往设置",
confirmText: "前往设置",
success(e) {
if (e.confirm) {
gotoAppPermissionSetting()
}
}
});
}
}
})
//跳转到**应用**的权限页面 参考来源https://ext.dcloud.net.cn/plugin?id=594
function gotoAppPermissionSetting() {
if (uni.getSystemInfoSync().platform == "ios") {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
var setting2 = NSURL2.URLWithString("app-settings:");
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
} else {
var Intent = plus.android.importClass("android.content.Intent");
var Settings = plus.android.importClass("android.provider.Settings");
var Uri = plus.android.importClass("android.net.Uri");
var mainActivity = plus.android.runtimeMainActivity();
var intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
intent.setData(uri);
mainActivity.startActivity(intent);
}
}
}

View File

@ -0,0 +1,76 @@
{
"id": "json-interceptor-chooseImage",
"displayName": "拦截器应用示例 — 图片选择",
"version": "1.0.2",
"description": "当选择图片遇到权限问题时引导用户快捷打开系统设置",
"keywords": [
"interceptor,拦截器,相册权限"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "n",
"Android Browser": "n",
"微信浏览器(Android)": "n",
"QQ浏览器(Android)": "n"
},
"H5-pc": {
"Chrome": "n",
"IE": "n",
"Edge": "n",
"Firefox": "n",
"Safari": "n"
},
"小程序": {
"微信": "n",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}

View File

@ -0,0 +1,30 @@
拦截器顾名思义,是在框架方法执行的各个环节(包含:拦截前触发、成功回调拦截、失败回调拦截、完成回调拦截)
插入逻辑,篡改参数或终止运行。
#### 优势
- 这种方式相当于改写了api的内部逻辑相比语法糖他更加直观不影响ide的代码提示。
- 将常规固定的逻辑封装到框架的api内且支持个性化设计。如你可以在本插件路径:`/uni_modules/json-interceptor-chooseImage/js_sdk/main.js`修改弹出框元素,绘制更漂亮的样式和文字说明。
#### 使用示例App.vue页代码如下
```
<script>
// #ifdef APP-PLUS
import interceptorChooseImage from '@/uni_modules/json-interceptor-chooseImage/js_sdk/main.js';
// #endif
export default {
onLaunch: function() {
console.log('App Launch')
// #ifdef APP-PLUS
interceptorChooseImage()
// #endif
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
```
> 跳转到**应用**的权限页面 参考来源:[https://ext.dcloud.net.cn/plugin?id=594](https://ext.dcloud.net.cn/plugin?id=594) 感谢作者@DCloud_heavensoft

View File

@ -0,0 +1,4 @@
## 1.0.12022-11-11
修改文档描述
## 1.0.02022-11-11
初次版本发布

View File

@ -0,0 +1,228 @@
<template>
<view class="start" v-if="show">
<view class="skip" :style="skipPositionStyle" @click="onSkip">跳过 {{time2}}</view>
<swiper class="swiper" :interval="interval" @change="onChangeSwiper">
<swiper-item v-for="(item, index) in list" :key="index">
<view class="swiper-item" :style="bgStyle">
<image class="image" :src="item" mode="widthFix"></image>
<view class="after" :style="afterStyle"></view>
</view>
</swiper-item>
<!-- autoplay -->
</swiper>
<view class="swiper-dot" v-if="list.length>1">
<view class="view" :style="index === current ? currentStyle : ''" :class="{'active': index === current}"
v-for="(item, index) in list" :key="index" />
</view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default () {
return []
},
required: true
},
hasTabbar: {
type: Boolean,
default: false,
},
hasNavbar: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 3000
},
time: {
type: Number,
default: 3
},
url: {
type: String,
default: ''
},
bgColor: {
type: String,
default: '#BB1219'
},
afterColor: {
type: String,
default: ''
},
currentColor: {
type: String,
default: '#BB1219'
}
},
data() {
return {
current: 0,
show: true,
timer: null,
time2: this.time
}
},
watch: {
time2(val) {
if (val <= 0) {
this.onSkip()
}
}
},
computed: {
bgStyle() {
return this.obj2strStyle({
'background-color': this.bgColor
})
},
afterStyle() {
return this.obj2strStyle({
'background-color': this.afterColor
})
},
currentStyle() {
return this.obj2strStyle({
'background-color': this.currentColor
})
},
skipPositionStyle() {
const {
statusBarHeight
} = uni.getSystemInfoSync()
if (!this.hasNavbar) {
return this.obj2strStyle({
'top': `${statusBarHeight*2 + 88 + 30}rpx`
})
}
return this.obj2strStyle({
'top': '30rpx'
})
}
},
mounted() {
if (this.hasTabbar) {
uni.hideTabBar()
}
this.timer = setInterval(() => {
this.time2--
}, 1000)
},
methods: {
obj2strStyle(obj) {
let style = ''
for (let key in obj) {
style += `${key}:${obj[key]};`
}
return style
},
onSkip() {
const {
url,
hasTabbar,
timer
} = this
clearTimeout(timer)
this.show = false
if (hasTabbar) {
uni.showTabBar()
}
if (url) {
uni.reLaunch({
url: url,
})
}
},
onChangeSwiper(e) {
this.current = e.detail.current
}
}
}
</script>
<style scoped>
/* $nav-height: 44px; */
.start {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
}
.skip {
position: absolute;
z-index: 2;
background-color: rgba(0, 0, 0, 0.1);
color: #fff;
right: 30rpx;
font-size: 28rpx;
width: 133rpx;
height: 60rpx;
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
}
.swiper {
height: 100vh;
width: 100vw;
}
.swiper-item {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background-color: transparent;
}
.swiper-item .after {
width: 100vw;
height: 500rpx;
position: absolute;
left: 0;
bottom: 0;
z-index: 1;
}
.swiper-item .image {
height: 100vh;
width: 100vw;
display: block;
position: relative;
z-index: 2;
}
.swiper-dot {
position: absolute;
width: 100vw;
left: 0;
bottom: 100rpx;
z-index: 3;
display: flex;
justify-content: center;
}
.swiper-dot .view {
width: 16rpx;
height: 16rpx;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.2);
margin: 0 12rpx;
}
.swiper-dot .view.active {
width: 30rpx;
border-radius: 24rpx;
}
</style>

View File

@ -0,0 +1,81 @@
{
"id": "m-start-ad",
"displayName": "start-ad开屏广告",
"version": "1.0.1",
"description": "自定义开屏广告",
"keywords": [
"start、开屏、开屏广告"
],
"repository": "",
"engines": {
"HBuilderX": "^3.2.12"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}

View File

@ -0,0 +1,78 @@
# m-start-ad
快速制作一个自定义开屏广告,支持轮播
### 属性说明
| 属性名 | 类型 | 默认值 | 必填 | 说明 |
| ------- | ------- | ------- | ------- | ------- |
| list | Array | [] | true | 开屏图片列表 |
| hasTabbar | Boolean | false | false | 页面存在原生tabbar组件会自动判断隐藏tabbar |
| hasNavbar | Boolean | false | false | 页面是否包含navbar |
| interval | Number | 3000 | false | 轮播自动播放时间(多图时可用) |
| time | Number | 3 | false | 倒计时跳过 |
| url | String | '' | false | 时间倒计时结束后跳转地址,如果是tabbar页面可为空 |
| bgColor | String | '#BB1219' | false | 页面的背景颜色, 开屏图片为自动垂直居中,在大屏幕手机下,有些时候高度不够的情况下,会造成上下留白,使用此属性可以填充背景色 |
| afterColor | String | '' | false | 页面底部背景色填充有些时候设置会设计渐变色图片如果只使用bgcolor无法解决需求此时定义此属性可在最下面填充颜色以达到颜色过度效果 |
| currentColor | String | '#BB1219' | false | 定义dot当前状态颜色, list长度大于1可见 |
### 如何做自定义开屏界面
#### 方式一
1、新建一个新页面 pages/start
2、修改pages.js配置文件把start地址放在最前
```
{
"pages": [
{
"path" : "pages/start", //page第一页就是开屏第一个页面
"style" :
{
"navigationBarTitleText": "",
"navigationStyle": "custom" //取消默认导航做到满屏效果
}
}
]
}
```
3、页面引入组件传入list, url
### 使用方式
```
<template>
<m-start-ad :list="list" url="pages/index/index" />
</template>
```
#### 方式二
1、页面关闭原生导航
```
{
"pages": [
{
"path" : "pages/index/index",
"style" :
{
"navigationBarTitleText": "",
"navigationStyle": "custom" //取消默认导航做到满屏效果
}
}
]
}
```
1、页面直接使用
```
<m-start-ad :list="list" />
```
2、倒计时结束后自动隐藏组件无需操作
### 方式对比
|对比 | 优点 | 缺点 |
| ------- | ------- |------- |
| 方式一 | 所有页面都可以公用 | 如果内页需要tabbar,需要自定义 |
| 方式二 | 按需配置 | 无法控制navbar需要自定义 |

View File

@ -0,0 +1,33 @@
## 1.2.22023-01-28
- 修复 运行/打包 控制台警告问题
## 1.2.12022-09-05
- 修复 当 text 超过 max-num 时badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473)
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
## 1.1.72021-11-08
- 优化 升级ui
- 修改 size 属性默认值调整为 small
- 修改 type 属性,默认值调整为 errorinfo 替换 default
## 1.1.62021-09-22
- 修复 在字节小程序上样式不生效的 bug
## 1.1.52021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.42021-07-29
- 修复 去掉 nvue 不支持css 的 align-self 属性nvue 下不暂支持 absolute 属性
## 1.1.32021-06-24
- 优化 示例项目
## 1.1.12021-05-12
- 新增 组件示例地址
## 1.1.02021-05-12
- 新增 uni-badge 的 absolute 属性,支持定位
- 新增 uni-badge 的 offset 属性,支持定位偏移
- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
- 优化 uni-badge 属性 custom-style 支持以对象形式自定义样式
## 1.0.72021-05-07
- 修复 uni-badge 在 App 端数字小于10时不是圆形的bug
- 修复 uni-badge 在父元素不是 flex 布局时宽度缩小的bug
- 新增 uni-badge 属性 custom-style 支持自定义样式
## 1.0.62021-02-04
- 调整为uni_modules目录规范

View File

@ -0,0 +1,268 @@
<template>
<view class="uni-badge--x">
<slot />
<text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]"
class="uni-badge" @click="onClick()">{{displayValue}}</text>
</view>
</template>
<script>
/**
* Badge 数字角标
* @description 数字角标一般和其它控件列表、9宫格等配合使用用于进行数量提示默认为实心灰色背景
* @tutorial https://ext.dcloud.net.cn/plugin?id=21
* @property {String} text 角标内容
* @property {String} size = [normal|small] 角标内容
* @property {String} type = [info|primary|success|warning|error] 颜色类型
* @value info 灰色
* @value primary 蓝色
* @value success 绿色
* @value warning 黄色
* @value error 红色
* @property {String} inverted = [true|false] 是否无需背景颜色
* @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+
* @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
* @value rightTop 右上
* @value rightBottom 右下
* @value leftTop 左上
* @value leftBottom 左下
* @property {Array[number]} offset 距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px[10, 10] 表示向 absolute 指定的内偏移 10px
* @property {String} isDot = [true|false] 是否显示为一个小点
* @event {Function} click 点击 Badge 触发事件
* @example <uni-badge text="1"></uni-badge>
*/
export default {
name: 'UniBadge',
emits: ['click'],
props: {
type: {
type: String,
default: 'error'
},
inverted: {
type: Boolean,
default: false
},
isDot: {
type: Boolean,
default: false
},
maxNum: {
type: Number,
default: 99
},
absolute: {
type: String,
default: ''
},
offset: {
type: Array,
default () {
return [0, 0]
}
},
text: {
type: [String, Number],
default: ''
},
size: {
type: String,
default: 'small'
},
customStyle: {
type: Object,
default () {
return {}
}
}
},
data() {
return {};
},
computed: {
width() {
return String(this.text).length * 8 + 12
},
classNames() {
const {
inverted,
type,
size,
absolute
} = this
return [
inverted ? 'uni-badge--' + type + '-inverted' : '',
'uni-badge--' + type,
'uni-badge--' + size,
absolute ? 'uni-badge--absolute' : ''
].join(' ')
},
positionStyle() {
if (!this.absolute) return {}
let w = this.width / 2,
h = 10
if (this.isDot) {
w = 5
h = 5
}
const x = `${- w + this.offset[0]}px`
const y = `${- h + this.offset[1]}px`
const whiteList = {
rightTop: {
right: x,
top: y
},
rightBottom: {
right: x,
bottom: y
},
leftBottom: {
left: x,
bottom: y
},
leftTop: {
left: x,
top: y
}
}
const match = whiteList[this.absolute]
return match ? match : whiteList['rightTop']
},
dotStyle() {
if (!this.isDot) return {}
return {
width: '10px',
minWidth: '0',
height: '10px',
padding: '0',
borderRadius: '10px'
}
},
displayValue() {
const {
isDot,
text,
maxNum
} = this
return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
}
},
methods: {
onClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" >
$uni-primary: #2979ff !default;
$uni-success: #4cd964 !default;
$uni-warning: #f0ad4e !default;
$uni-error: #dd524d !default;
$uni-info: #909399 !default;
$bage-size: 12px;
$bage-small: scale(0.8);
.uni-badge--x {
/* #ifdef APP-NVUE */
// align-self: flex-start;
/* #endif */
/* #ifndef APP-NVUE */
display: inline-block;
/* #endif */
position: relative;
}
.uni-badge--absolute {
position: absolute;
}
.uni-badge--small {
transform: $bage-small;
transform-origin: center center;
}
.uni-badge {
/* #ifndef APP-NVUE */
display: flex;
overflow: hidden;
box-sizing: border-box;
font-feature-settings: "tnum";
min-width: 20px;
/* #endif */
justify-content: center;
flex-direction: row;
height: 20px;
padding: 0 4px;
line-height: 18px;
color: #fff;
border-radius: 100px;
background-color: $uni-info;
background-color: transparent;
border: 1px solid #fff;
text-align: center;
font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-size: $bage-size;
/* #ifdef H5 */
z-index: 999;
cursor: pointer;
/* #endif */
&--info {
color: #fff;
background-color: $uni-info;
}
&--primary {
background-color: $uni-primary;
}
&--success {
background-color: $uni-success;
}
&--warning {
background-color: $uni-warning;
}
&--error {
background-color: $uni-error;
}
&--inverted {
padding: 0 5px 0 0;
color: $uni-info;
}
&--info-inverted {
color: $uni-info;
background-color: transparent;
}
&--primary-inverted {
color: $uni-primary;
background-color: transparent;
}
&--success-inverted {
color: $uni-success;
background-color: transparent;
}
&--warning-inverted {
color: $uni-warning;
background-color: transparent;
}
&--error-inverted {
color: $uni-error;
background-color: transparent;
}
}
</style>

View File

@ -0,0 +1,85 @@
{
"id": "uni-badge",
"displayName": "uni-badge 数字角标",
"version": "1.2.2",
"description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
"keywords": [
"",
"badge",
"uni-ui",
"uniui",
"数字角标",
"徽章"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,10 @@
## Badge 数字角标
> **组件名uni-badge**
> 代码块: `uBadge`
数字角标一般和其它控件列表、9宫格等配合使用用于进行数量提示默认为实心灰色背景
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,6 @@
## 0.1.22022-06-08
- 修复 微信小程序 separator 不显示问题
## 0.1.12022-06-02
- 新增 支持 uni.scss 修改颜色
## 0.1.02022-04-21
- 初始化

View File

@ -0,0 +1,121 @@
<template>
<view class="uni-breadcrumb-item">
<view :class="{
'uni-breadcrumb-item--slot': true,
'uni-breadcrumb-item--slot-link': to && currentPage !== to
}" @click="navTo">
<slot />
</view>
<i v-if="separatorClass" class="uni-breadcrumb-item--separator" :class="separatorClass" />
<text v-else class="uni-breadcrumb-item--separator">{{ separator }}</text>
</view>
</template>
<script>
/**
* BreadcrumbItem 面包屑导航子组件
* @property {String/Object} to 路由跳转页面路径/对象
* @property {Boolean} replace 在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录(仅 h5 支持)
*/
export default {
data() {
return {
currentPage: ""
}
},
options: {
virtualHost: true
},
props: {
to: {
type: String,
default: ''
},
replace:{
type: Boolean,
default: false
}
},
inject: {
uniBreadcrumb: {
from: "uniBreadcrumb",
default: null
}
},
created(){
const pages = getCurrentPages()
const page = pages[pages.length-1]
if(page){
this.currentPage = `/${page.route}`
}
},
computed: {
separator() {
return this.uniBreadcrumb.separator
},
separatorClass() {
return this.uniBreadcrumb.separatorClass
}
},
methods: {
navTo() {
const { to } = this
if (!to || this.currentPage === to){
return
}
if(this.replace){
uni.redirectTo({
url:to
})
}else{
uni.navigateTo({
url:to
})
}
}
}
}
</script>
<style lang="scss">
$uni-primary: #2979ff !default;
$uni-base-color: #6a6a6a !default;
$uni-main-color: #3a3a3a !default;
.uni-breadcrumb-item {
display: flex;
align-items: center;
white-space: nowrap;
font-size: 14px;
&--slot {
color: $uni-base-color;
padding: 0 10px;
&-link {
color: $uni-main-color;
font-weight: bold;
/* #ifndef APP-NVUE */
cursor: pointer;
/* #endif */
&:hover {
color: $uni-primary;
}
}
}
&--separator {
font-size: 12px;
color: $uni-base-color;
}
&:first-child &--slot {
padding-left: 0;
}
&:last-child &--separator {
display: none;
}
}
</style>

View File

@ -0,0 +1,41 @@
<template>
<view class="uni-breadcrumb">
<slot />
</view>
</template>
<script>
/**
* Breadcrumb 面包屑导航父组件
* @description 显示当前页面的路径,快速返回之前的任意页面
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} separator 分隔符,默认为斜杠'/'
* @property {String} separatorClass 图标分隔符 class
*/
export default {
options: {
virtualHost: true
},
props: {
separator: {
type: String,
default: '/'
},
separatorClass: {
type: String,
default: ''
}
},
provide() {
return {
uniBreadcrumb: this
}
}
}
</script>
<style lang="scss">
.uni-breadcrumb {
display: flex;
}
</style>

View File

@ -0,0 +1,85 @@
{
"id": "uni-breadcrumb",
"displayName": "uni-breadcrumb 面包屑",
"version": "0.1.2",
"description": "Breadcrumb 面包屑",
"keywords": [
"uni-breadcrumb",
"breadcrumb",
"uni-ui",
"面包屑导航",
"面包屑"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,66 @@
## breadcrumb 面包屑导航
> **组件名uni-breadcrumb**
> 代码块: `ubreadcrumb`
显示当前页面的路径,快速返回之前的任意页面。
### 安装方式
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
### 基本用法
在 ``template`` 中使用组件
```html
<uni-breadcrumb separator="/">
<uni-breadcrumb-item v-for="(route,index) in routes" :key="index" :to="route.to">{{route.name}}</uni-breadcrumb-item>
</uni-breadcrumb>
```
```js
export default {
name: "uni-stat-breadcrumb",
data() {
return {
routes: [{
to: '/A',
name: 'A页面'
}, {
to: '/B',
name: 'B页面'
}, {
to: '/C',
name: 'C页面'
}]
};
}
}
```
## API
### Breadcrumb Props
|属性名 |类型 |默认值 |说明 |
|:-: |:-: |:-: |:-: |
|separator |String |斜杠'/' |分隔符 |
|separatorClass |String | |图标分隔符 class |
### Breadcrumb Item Props
|属性名 |类型 |默认值 |说明 |
|:-: |:-: |:-: |:-: |
|to |String | |路由跳转页面路径 |
|replace|Boolean | |在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录(仅 h5 支持) |
## 组件示例
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb](https://hellouniapp.dcloud.net.cn/pages/extUI/breadcrumb/breadcrumb)

View File

@ -0,0 +1,28 @@
## 1.4.112024-01-10
- 修复 回到今天时,月份显示不一致问题
## 1.4.102023-04-10
- 修复 某些情况 monthSwitch 未触发的Bug
## 1.4.92023-02-02
- 修复 某些情况切换月份错误的Bug
## 1.4.82023-01-30
- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964)
## 1.4.72022-09-16
- 优化 支持使用 uni-scss 控制主题色
## 1.4.62022-09-08
- 修复 表头年月切换导致改变当前日期为选择月1号且未触发change事件的Bug
## 1.4.52022-02-25
- 修复 条件编译 nvue 不支持的 css 样式的Bug
## 1.4.42022-02-25
- 修复 条件编译 nvue 不支持的 css 样式的Bug
## 1.4.32021-09-22
- 修复 startDate、 endDate 属性失效的Bug
## 1.4.22021-08-24
- 新增 支持国际化
## 1.4.12021-08-05
- 修复 弹出层被 tabbar 遮盖的Bug
## 1.4.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.3.162021-05-12
- 新增 组件示例地址
## 1.3.152021-02-04
- 调整为uni_modules目录规范

View File

@ -0,0 +1,546 @@
/**
* @1900-2100区间内的公历、农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hex、Attribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function (y) {
var i; var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function (y) { // 闰字编码 \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (0、29、30)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function (y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月非闰月的总天数计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-1、29、30)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function (y, m) {
if (m > 12 || m < 1) { return -1 }// 月份参数从1至12参数错误返回-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-1、28、29、30、31)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function (y, m) {
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var ms = m - 1
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function (lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月、日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function (cMonth, cDay) {
var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function (offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100)n二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
*/
getTerm: function (y, n) {
if (y < 1900 || y > 2100) { return -1 }
if (n < 1 || n > 24) { return -1 }
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
*/
toChinaMonth: function (m) { // 月 => \u6708
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var s = this.nStr3[m - 1]
s += '\u6708'// 加上月字
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
*/
toChinaDay: function (d) { // 日 => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341'; break
case 20:
s = '\u4e8c\u5341'; break
break
case 30:
s = '\u4e09\u5341'; break
break
default :
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
*/
getAnimal: function (y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
// 年份限定、上限
if (y < 1900 || y > 2100) {
return -1// undefined转换为数字变为NaN
}
// 公历传参最下限
if (y == 1900 && m == 1 && d < 31) {
return -1
}
// 未传参 获得当天
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i; var leap = 0; var temp = 0
// 修正ymd参数
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp; i--
}
// 是否今天
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
// 星期几
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
// 数字表示周几顺应天朝周一开始的惯例
if (nWeek == 0) {
nWeek = 7
}
// 农历年
var year = i
var leap = this.leapMonth(i) // 闰哪个月
var isLeap = false
// 效验闰月
for (i = 1; i < 13 && offset > 0; i++) {
// 闰月
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
} else {
temp = this.monthDays(year, i)// 计算农历普通月天数
}
// 解除闰月
if (isLeap == true && i == (leap + 1)) { isLeap = false }
offset -= temp
}
// 闰月导致数组下标重叠取反
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true; --i
}
}
if (offset < 0) {
offset += temp; --i
}
// 农历月
var month = i
// 农历日
var day = offset + 1
// 天干地支处理
var sm = m - 1
var gzY = this.toGanZhiYear(year)
// 当月的两个节气
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
// 依据12节气修正干支月
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
// 传入的日期的节气与否
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
// 日柱 当月一日与 1900/1/1 相差天数
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
// 该日期所属的星座
var astro = this.toAstro(m, d)
return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
// 计算农历的时间差
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0; var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { // 处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(y); isAdd = true
}
}
offset += this.monthDays(y, i)
}
// 转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) { offset += day }
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar

View File

@ -0,0 +1,12 @@
{
"uni-calender.ok": "ok",
"uni-calender.cancel": "cancel",
"uni-calender.today": "today",
"uni-calender.MON": "MON",
"uni-calender.TUE": "TUE",
"uni-calender.WED": "WED",
"uni-calender.THU": "THU",
"uni-calender.FRI": "FRI",
"uni-calender.SAT": "SAT",
"uni-calender.SUN": "SUN"
}

View File

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

View File

@ -0,0 +1,12 @@
{
"uni-calender.ok": "确定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

View File

@ -0,0 +1,12 @@
{
"uni-calender.ok": "確定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

View File

@ -0,0 +1,187 @@
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}"
@click="choiceDate(weeks)">
<view class="uni-calendar-item__weeks-box-item">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text" :class="{
'uni-calendar-item--isDay-text': weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.date}}</text>
<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}">{{todayText}}</text>
<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--extra':weeks.extraInfo.info,
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
export default {
emits:['change'],
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
}
},
computed: {
todayText() {
return t("uni-calender.today")
},
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
$uni-font-size-base:14px;
$uni-text-color:#333;
$uni-font-size-sm:12px;
$uni-color-error: #e43d33;
$uni-opacity-disabled: 0.3;
$uni-text-color-disable:#c0c0c0;
$uni-primary: #2979ff !default;
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
}
.uni-calendar-item__weeks-box-text {
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar-item__weeks-lunar-text {
font-size: $uni-font-size-sm;
color: $uni-text-color;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 100rpx;
height: 100rpx;
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: $uni-color-error;
}
.uni-calendar-item--disable {
background-color: rgba(249, 249, 249, $uni-opacity-disabled);
color: $uni-text-color-disable;
}
.uni-calendar-item--isDay-text {
color: $uni-primary;
}
.uni-calendar-item--isDay {
background-color: $uni-primary;
opacity: 0.8;
color: #fff;
}
.uni-calendar-item--extra {
color: $uni-color-error;
opacity: 0.8;
}
.uni-calendar-item--checked {
background-color: $uni-primary;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--multiple {
background-color: $uni-primary;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--before-checked {
background-color: #ff5a5f;
color: #fff;
}
.uni-calendar-item--after-checked {
background-color: #ff5a5f;
color: #fff;
}
</style>

View File

@ -0,0 +1,567 @@
<template>
<view class="uni-calendar">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
<view class="uni-calendar__header-btn-box" @click="close">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
</view>
<view class="uni-calendar__header-btn-box" @click="confirm">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
</view>
</view>
<view class="uni-calendar__header">
<view class="uni-calendar__header-btn-box" @click.stop="pre">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<picker mode="date" :value="date" fields="month" @change="bindDateChange">
<text class="uni-calendar__header-text">{{ (nowDate.year||'') +' / '+( nowDate.month||'')}}</text>
</picker>
<view class="uni-calendar__header-btn-box" @click.stop="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<text class="uni-calendar__backtoday" @click="backToday">{{todayText}}</text>
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{monText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import CalendarItem from './uni-calendar-item.vue'
import { initVueI18n } from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const { t } = initVueI18n(i18nMessages)
/**
* Calendar 日历
* @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间,默认为今天
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} range 范围选择
* @property {Boolean} insert = [true|false] 插入模式,默认为false
* @value true 弹窗模式
* @value false 插入模式
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
* @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
* @property {Boolean} showMonth 是否选择月份为背景
* @event {Function} change 日期改变,`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
*/
export default {
components: {
CalendarItem
},
emits:['close','confirm','change','monthSwitch'],
props: {
date: {
type: String,
default: ''
},
selected: {
type: Array,
default () {
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
},
clearDate: {
type: Boolean,
default: true
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false
}
},
computed:{
/**
* for i18n
*/
okText() {
return t("uni-calender.ok")
},
cancelText() {
return t("uni-calender.cancel")
},
todayText() {
return t("uni-calender.today")
},
monText() {
return t("uni-calender.MON")
},
TUEText() {
return t("uni-calender.TUE")
},
WEDText() {
return t("uni-calender.WED")
},
THUText() {
return t("uni-calender.THU")
},
FRIText() {
return t("uni-calender.FRI")
},
SATText() {
return t("uni-calender.SAT")
},
SUNText() {
return t("uni-calender.SUN")
},
},
watch: {
date(newVal) {
// this.cale.setDate(newVal)
this.init(newVal)
},
startDate(val){
this.cale.resetSatrtDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val){
this.cale.resetEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
}
},
created() {
this.cale = new Calendar({
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
this.init(this.date)
},
methods: {
// 取消穿透
clean() {},
bindDateChange(e) {
const value = e.detail.value + '-1'
this.setDate(value)
const { year,month } = this.cale.getDate(value)
this.$emit('monthSwitch', {
year,
month
})
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
},
/**
* 打开日历弹窗
*/
open() {
// 弹窗模式并且清理数据
if (this.clearDate && !this.insert) {
this.cale.cleanMultipleStatus()
// this.cale.setDate(this.date)
this.init(this.date)
}
this.show = true
this.$nextTick(() => {
setTimeout(() => {
this.aniMaskShow = true
}, 50)
})
},
/**
* 关闭日历弹窗
*/
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
this.$emit('close')
}, 300)
})
},
/**
* 确认按钮
*/
confirm() {
this.setEmit('confirm')
this.close()
},
/**
* 变化触发
*/
change() {
if (!this.insert) return
this.setEmit('change')
},
/**
* 选择月份触发
*/
monthSwitch() {
let {
year,
month
} = this.nowDate
this.$emit('monthSwitch', {
year,
month: Number(month)
})
},
/**
* 派发事件
* @param {Object} name
*/
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
/**
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
// 设置多选
this.cale.setMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
this.change()
},
/**
* 回到今天
*/
backToday() {
const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
const date = this.cale.getDate(new Date())
const todayYearMonth = `${date.year}-${date.month}`
this.init(date.fullDate)
if(nowYearMonth !== todayYearMonth) {
this.monthSwitch()
}
this.change()
},
/**
* 上个月
*/
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
},
/**
* 下个月
*/
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.monthSwitch()
},
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
}
}
}
</script>
<style lang="scss" scoped>
$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
$uni-border-color: #EDEDED;
$uni-text-color: #333;
$uni-bg-color-hover:#f1f1f1;
$uni-font-size-base:14px;
$uni-text-color-placeholder: #808080;
$uni-color-subtitle: #555555;
$uni-text-color-grey:#999;
.uni-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--mask-show {
opacity: 1
}
.uni-calendar--fixed {
position: fixed;
/* #ifdef APP-NVUE */
bottom: 0;
/* #endif */
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(460px);
/* #ifndef APP-NVUE */
bottom: calc(var(--window-bottom));
z-index: 99;
/* #endif */
}
.uni-calendar--ani-show {
transform: translateY(0);
}
.uni-calendar__content {
background-color: #fff;
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
}
.uni-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 5px;
padding-left: 10px;
height: 25px;
line-height: 25px;
font-size: 12px;
border-top-left-radius: 25px;
border-bottom-left-radius: 25px;
color: $uni-text-color;
background-color: $uni-bg-color-hover;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.uni-calendar__header-btn {
width: 10px;
height: 10px;
border-left-color: $uni-text-color-placeholder;
border-left-style: solid;
border-left-width: 2px;
border-top-color: $uni-color-subtitle;
border-top-style: solid;
border-top-width: 2px;
}
.uni-calendar--left {
transform: rotate(-45deg);
}
.uni-calendar--right {
transform: rotate(135deg);
}
.uni-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-calendar__weeks-item {
flex: 1;
}
.uni-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 45px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 14px;
}
.uni-calendar__box {
position: relative;
}
.uni-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.uni-calendar__box-bg-text {
font-size: 200px;
font-weight: bold;
color: $uni-text-color-grey;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
</style>

View File

@ -0,0 +1,360 @@
import CALENDAR from './calendar.js'
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range
} = {}) {
// 当前日期
this.date = this.getDate(new Date()) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
this.startDate = startDate
// 范围结束
this.endDate = endDate
this.range = range
// 多选状态
this.cleanMultipleStatus()
// 每周日期
this.weeks = {}
// this._getWeek(this.date.fullDate)
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.selectDate = this.getDate(date)
this._getWeek(this.selectDate.fullDate)
}
/**
* 清理多选状态
*/
cleanMultipleStatus() {
this.multipleStatus = {
before: '',
after: '',
data: []
}
}
/**
* 重置开始日期
*/
resetSatrtDate(startDate) {
// 范围开始
this.startDate = startDate
}
/**
* 重置结束日期
*/
resetEndDate(endDate) {
// 范围结束
this.endDate = endDate
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31 && AddDayCount>0) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
const preMonth = dd.getMonth()
dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
const nextMonth = dd.getMonth()
// 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if(AddDayCount<0 && preMonth!==0 && nextMonth-preMonth>AddDayCount){
dd.setMonth(nextMonth+(nextMonth-preMonth+AddDayCount))
}
// 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
if(AddDayCount>0 && nextMonth-preMonth>AddDayCount){
dd.setMonth(nextMonth-(nextMonth-preMonth-AddDayCount))
}
}
break
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
lunar: this.getlunar(full.year, full.month - 1, beforeDate),
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDys(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
// 是否今天
let isDay = fullDate === nowDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
disableBefore = this.dateCompare(this.startDate, nowDate)
}
if (this.endDate) {
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
disableAfter = this.dateCompare(nowDate, this.endDate)
}
let multiples = this.multipleStatus.data
let checked = false
let multiplesStatus = -1
if (this.range) {
if (multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, nowDate)
})
}
if (multiplesStatus !== -1) {
checked = true
}
}
let data = {
fullDate: nowDate,
year: full.year,
date: i,
multiple: this.range ? checked : false,
beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate),
afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate),
month: full.month,
lunar: this.getlunar(full.year, full.month, i),
disable: !(disableBefore && disableAfter),
isDay
}
if (info) {
data.extraInfo = info
}
dateArr.push(data)
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
lunar: this.getlunar(full.year, Number(full.month) + 1, i),
disable: true
})
}
return dateArr
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
geDateAll(begin, end) {
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var unixDb = db.getTime() - 24 * 60 * 60 * 1000
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = unixDb; k <= unixDe;) {
k = k + 24 * 60 * 60 * 1000
arr.push(this.getDate(new Date(parseInt(k))).fullDate)
}
return arr
}
/**
* 计算阴历日期显示
*/
getlunar(year, month, date) {
return CALENDAR.solar2lunar(year, month, date)
}
/**
* 设置打点
*/
setSelectInfo(data, value) {
this.selected = value
this._getWeek(data)
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (before && after) {
this.multipleStatus.before = ''
this.multipleStatus.after = ''
this.multipleStatus.data = []
} else {
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
}
}
}
this._getWeek(fullDate)
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData) {
const {
year,
month
} = this.getDate(dateData)
let firstDay = new Date(year, month - 1, 1).getDay()
let currentDay = new Date(year, month, 0).getDate()
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
nextMonthDays: [], // 下个月开始几天
weeks: []
}
let canlender = []
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
let weeks = {}
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
this.canlender = canlender
this.weeks = weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar

View File

@ -0,0 +1,85 @@
{
"id": "uni-calendar",
"displayName": "uni-calendar 日历",
"version": "1.4.11",
"description": "日历组件",
"keywords": [
"uni-ui",
"uniui",
"日历",
"",
"打卡",
"日历选择"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,103 @@
## Calendar 日历
> **组件名uni-calendar**
> 代码块: `uCalendar`
日历组件
> **注意事项**
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js)
> - 仅支持自定义组件模式
> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date()
> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意
> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动
### 安装方式
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
### 基本用法
在 ``template`` 中使用组件
```html
<view>
<uni-calendar
:insert="true"
:lunar="true"
:start-date="'2019-3-2'"
:end-date="'2019-5-20'"
@change="change"
/>
</view>
```
### 通过方法打开日历
需要设置 `insert` 为 `false`
```html
<view>
<uni-calendar
ref="calendar"
:insert="false"
@confirm="confirm"
/>
<button @click="open">打开日历</button>
</view>
```
```javascript
export default {
data() {
return {};
},
methods: {
open(){
this.$refs.calendar.open();
},
confirm(e) {
console.log(e);
}
}
};
```
## API
### Calendar Props
| 属性名 | 类型 | 默认值| 说明 |
| - | - | - | - |
| date | String |- | 自定义当前时间,默认为今天 |
| lunar | Boolean | false | 显示农历 |
| startDate | String |- | 日期选择范围-开始日期 |
| endDate | String |- | 日期选择范围-结束日期 |
| range | Boolean | false | 范围选择 |
| insert | Boolean | false | 插入模式,可选值ture插入模式false弹窗模式默认为插入模式 |
|clearDate |Boolean |true |弹窗模式是否清空上次选择内容 |
| selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
|showMonth | Boolean | true | 是否显示月份为背景 |
### Calendar Events
| 事件名 | 说明 |返回值|
| - | - | - |
| open | 弹出日历组件,`insert :false` 时生效|- |
## 组件示例
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar)

View File

@ -0,0 +1,49 @@
## 0.7.52023-12-18
- 修复 在uni-app x项目部分情况下执行uni-captcha组件的setFocus无效的问题
## 0.7.42023-12-18
- 更新 `package.json` -> `dependencies` 增加 `uni-popup`
## 0.7.32023-11-15
- 更新 uni-popup-captcha.uvue依赖的popup组件直接使用uni_modules下的uni-popup组件
## 0.7.22023-11-07
- 新增 前端组件uni-captcha.uvue、uni-popup-captcha
## 0.7.12023-11-07
- 新增 前端组件uni-captcha.uvue、uni-popup-captcha
## 0.7.02023-10-10
- 新增 支持在`uni-config-center`中配置mode可选值为svg和bmp配置成bmp后可以在uniappx的uvue页面正常显示验证码uvue不支持显示svg验证码
## 0.6.42023-01-16
- 修复 部分情况下APP端无法获取验证码的问题
## 0.6.32023-01-11
- 修复 抖音小程序无法显示的Bug
- 修复 刷新时兼容 device_uuid
## 0.6.12022-06-23
- 修复:部分返回值,不符合响应体规范的问题
## 0.6.02022-05-27
- 新增:支持在`uni-config-center`中根据场景值配置
- 修复:弹窗式验证码,输入内容后点击取消,重新打开验证码的值仍然存在的问题
## 0.5.22022-05-19
- 修复在Vue3的兼容问题
## 0.5.12022-05-18
- 修复在某些情况下微信小程序端验证码显示错误的问题
## 0.5.02022-05-17
- 新增支持在`uni-captcha-co`->`config`配置验证码
## 0.4.12022-05-16
- 新增示例项目
## 0.4.02022-05-16
- 集成创建、刷新、显示验证码的云端一体验证码组件
- 云对象`uni-captcha-co`集成获取验证码的api`getImageCaptcha`
## 0.3.12022-05-13
- 新增 返回值符合响应体规范
## 0.3.02022-05-13
- 新增 支持 uni-config-center 配置
## 0.2.22022-04-25
- 修复 0.2.1 版本引起的使用 image 组件验证码不显示的Bug
## 0.2.12022-04-18
- 更新 优化字体
## 0.2.02022-04-14
- 新增 使用 svg 表现形式更好
- 新增 使用字体,可以任意替换默认字体
- 新增 支持设置字体大小
- 新增 支持忽略某些字符
- 注意 更新之后请重新上传公共模块
## 0.1.02021-03-01
- 调整为uni_modules目录规范

View File

@ -0,0 +1,180 @@
<template>
<view class="captcha-box">
<view class="captcha-img-box">
<image class="loding" src="/uni_modules/uni-captcha/static/run.gif" v-if="loging" mode="widthFix" />
<image class="captcha-img" :class="{opacity:loging}" @click="getImageCaptcha(true)" :src="captchaBase64" mode="widthFix" />
</view>
<input @blur="focusCaptchaInput = false" @focus="focusCaptchaInput = true" :focus="focusCaptchaInput" type="digit" class="captcha" :inputBorder="false"
maxlength="4" v-model="val" placeholder="请输入验证码" :cursor-spacing="cursorSpacing" />
</view>
</template>
<script>
export default {
emits: ["modelValue"],
props: {
cursorSpacing: {
type: Number,
default: 100
},
modelValue: {
type: String,
default: ""
},
value: {
type: String,
default: ""
},
scene: {
type: String,
default: ""
},
focus: {
type: Boolean,
default: false
}
},
data() {
return {
focusCaptchaInput: false,
captchaBase64: "" as string,
loging: false,
val: ""
};
},
watch: {
value: {
handler(value : string) {
// console.log('setvue', value);
this.val = value
},
immediate: true
},
modelValue: {
handler(modelValue : string) {
// console.log('setvue', modelValue);
this.val = modelValue
},
immediate: true
},
scene: {
handler(scene : string) {
if (scene.length != 0) {
this.getImageCaptcha(this.focus)
} else {
uni.showToast({
title: 'scene不能为空',
icon: 'none'
});
}
},
immediate: true
},
val(value : string) {
// console.log('setvue', value);
// TODO 兼容 vue2
// #ifdef VUE2
this.$emit('input', value);
// #endif
// TODO 兼容 vue3
// #ifdef VUE3
this.$emit('update:modelValue', value)
// #endif
}
},
methods: {
setFocus(state:boolean){
this.focusCaptchaInput = state
},
getImageCaptcha(focus : boolean) {
this.loging = true
if (focus) {
this.val = ''
this.focusCaptchaInput = true
}
const uniIdCo = uniCloud.importObject("uni-captcha-co", {
customUI: true
})
uniIdCo.getImageCaptcha({
scene: this.scene,
isUniAppX:true
}).then((result : UTSJSONObject) => {
this.captchaBase64 = (result.getString('captchaBase64') as string)
})
.catch<void>((err : any | null) : void => {
const error = err as UniCloudError
console.error(error)
console.error(error.code)
uni.showToast({
title: error.message,
icon: 'none'
});
})
.finally(()=> {
this.loging = false
})
}
}
}
</script>
<style lang="scss" scoped>
.captcha-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: flex-end;
flex: 1;
}
.captcha-img-box,
.captcha {
height: 44px;
}
.captcha {
flex: 1;
}
.captcha-img-box {
position: relative;
background-color: #FEFAE7;
}
.captcha {
background-color: #F8F8F8;
font-size: 14px;
flex: 1;
padding: 0 20rpx;
margin-left: 20rpx;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.captcha-img-box,
.captcha-img,
.loding {
width: 100px;
}
.captcha-img {
/* #ifdef WEB */
cursor: pointer;
/* #endif */
height: 44px;
}
.loding {
z-index: 9;
position: absolute;
width: 30px;
margin:7px 35px;
}
.opacity {
opacity: 0.5;
}
</style>

View File

@ -0,0 +1,167 @@
<template>
<view class="captcha-box">
<view class="captcha-img-box">
<uni-icons class="loding" size="20px" color="#BBB" v-if="loging" type="spinner-cycle"></uni-icons>
<image class="captcha-img" :class="{opacity:loging}" @click="getImageCaptcha" :src="captchaBase64"
mode="widthFix"></image>
</view>
<input @blur="focusCaptchaInput = false" :focus="focusCaptchaInput" type="text" class="captcha"
:inputBorder="false" maxlength="4" v-model="val" placeholder="请输入验证码">
</view>
</template>
<script>
export default {
props: {
modelValue:String,
value:String,
scene: {
type: String,
default () {
return ""
}
},
focus: {
type: Boolean,
default () {
return false
}
}
},
computed:{
val:{
get(){
return this.value||this.modelValue
},
set(value){
// console.log(value);
// TODO 兼容 vue2
// #ifdef VUE2
this.$emit('input', value);
// #endif
// TODO 兼容 vue3
// #ifdef VUE3
this.$emit('update:modelValue', value)
// #endif
}
}
},
data() {
return {
focusCaptchaInput: false,
captchaBase64: "",
loging: false
};
},
watch: {
scene: {
handler(scene) {
if (scene) {
this.getImageCaptcha(this.focus)
} else {
uni.showToast({
title: 'scene不能为空',
icon: 'none'
});
}
},
immediate:true
}
},
methods: {
getImageCaptcha(focus = true) {
this.loging = true
if (focus) {
this.val = ''
this.focusCaptchaInput = true
}
const uniIdCo = uniCloud.importObject("uni-captcha-co", {
customUI: true
})
uniIdCo.getImageCaptcha({
scene: this.scene
}).then(result => {
// console.log(result);
this.captchaBase64 = result.captchaBase64
})
.catch(e => {
uni.showToast({
title: e.message,
icon: 'none'
});
}).finally(e => {
this.loging = false
})
}
}
}
</script>
<style lang="scss" scoped>
.captcha-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: flex-end;
flex: 1;
}
.captcha-img-box,
.captcha {
height: 44px;
line-height: 44px;
}
.captcha-img-box {
position: relative;
background-color: #FEFAE7;
}
.captcha {
background-color: #F8F8F8;
font-size: 14px;
flex: 1;
padding: 0 20rpx;
margin-left: 20rpx;
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.captcha-img-box,
.captcha-img,
.loding {
height: 44px !important;
width: 100px;
}
.captcha-img{
cursor: pointer;
}
.loding {
z-index: 9;
color: #bbb;
position: absolute;
text-align: center;
line-height: 45px;
animation: rotate 1s linear infinite;
}
.opacity {
opacity: 0.5;
}
@keyframes rotate {
from {
transform: rotate(0deg)
}
to {
transform: rotate(360deg)
}
}
</style>

View File

@ -0,0 +1,130 @@
<template>
<uni-popup ref="popup" @clickMask="cancel">
<view class="popup-captcha">
<view class="content">
<text class="title">{{title}}</text>
<uni-captcha ref="captcha" :focus="focus" :scene="scene" v-model="val" :cursorSpacing="150"></uni-captcha>
</view>
<view class="button-box">
<text @click="cancel" class="btn cancel">取消</text>
<text @click="confirm" class="btn confirm">确认</text>
</view>
</view>
</uni-popup>
</template>
<script>
let confirmCallBack = ():void=>console.log('未传入回调函数')
export default {
emits:["modelValue","confirm","cancel"],
data() {
return {
focus: false,
val:""
}
},
props: {
modelValue: {
type: String,
default: ""
},
value: {
type: String,
default: ""
},
scene: {
type: String,
default: ""
},
title: {
type: String,
default: "默认标题"
}
},
watch: {
val(val:string) {
// console.log(val);
// TODO 兼容 vue2
// #ifdef VUE2
this.$emit('input', val);
// #endif
// TODO 兼容 vue3
// #ifdef VUE3
this.$emit('update:modelValue', val)
// #endif
if(val.length == 4){
this.confirm()
}
}
},
mounted() {},
methods: {
open(callback: () => void) {
// console.log('callback',callback);
confirmCallBack = callback;
this.focus = true
this.val = "";
(this.$refs['popup'] as ComponentPublicInstance).$callMethod("open");
this.$nextTick(()=>{
(this.$refs['captcha'] as ComponentPublicInstance).$callMethod("getImageCaptcha",true);
})
},
close() {
this.focus = false;
(this.$refs['popup'] as ComponentPublicInstance).$callMethod("close");
},
cancel(){
this.close()
this.$emit("cancel")
},
confirm() {
if (this.val.length != 4) {
return uni.showToast({
title: '请填写验证码',
icon: 'none'
});
}
this.close()
this.$emit('confirm')
confirmCallBack()
}
}
}
</script>
<style lang="scss" scoped>
.popup-captcha {
background-color: #fff;
flex-direction: column;
width: 600rpx;
padding:10px 15px;
border-radius: 10px;
}
.popup-captcha .title {
text-align: center;
font-weight: 700;
margin: 5px 0;
}
.popup-captcha .button-box {
flex-direction: row;
justify-content: space-around;
margin-top: 5px;
}
.popup-captcha .button-box .btn {
flex: 1;
height: 35px;
line-height: 35px;
text-align: center;
}
.popup-captcha .button-box .cancel {
border: 1px solid #eee;
color: #666;
}
.confirm {
background-color: #0070ff;
color: #fff;
margin-left: 5px;
}
</style>

View File

@ -0,0 +1,140 @@
<template>
<uni-popup ref="popup" type="center">
<view class="popup-captcha">
<view class="content">
<text class="title">{{title}}</text>
<uni-captcha :focus="focus" :scene="scene" v-model="val"></uni-captcha>
</view>
<view class="button-box">
<view @click="close" class="btn">取消</view>
<view @click="confirm" class="btn confirm">确认</view>
</view>
</view>
</uni-popup>
</template>
<script>
export default {
data() {
return {
focus: false
}
},
props: {
modelValue:String,
value:String,
scene: {
type: String,
default () {
return ""
}
},
title: {
type: String,
default () {
return ""
}
},
},
computed:{
val:{
get(){
return this.value||this.modelValue
},
set(value){
// console.log(value);
// TODO 兼容 vue2
// #ifdef VUE2
this.$emit('input', value);
// #endif
// TODO 兼容 vue3
// #ifdef VUE3
this.$emit('update:modelValue', value)
// #endif
}
}
},
methods: {
open() {
this.focus = true
this.val = ""
this.$refs.popup.open()
},
close() {
this.focus = false
this.$refs.popup.close()
},
confirm() {
if(!this.val){
return uni.showToast({
title: '请填写验证码',
icon: 'none'
});
}
this.close()
this.$emit('confirm')
}
}
}
</script>
<style lang="scss" scoped>
/* #ifndef APP-NVUE */
view {
display: flex;
flex-direction: column;
}
/* #endif */
.popup-captcha {
/* #ifndef APP-NVUE */
display: flex;
max-width: 600px;
/* #endif */
width: 600rpx;
padding-bottom: 0;
background-color: #FFF;
border-radius: 10px;
flex-direction: column;
position: relative;
}
.popup-captcha .content {
padding: 1.3em 0.8em;
}
.popup-captcha .title {
text-align: center;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
font-weight: 400;
font-size: 18px;
overflow: hidden;
text-overflow: ellipsis;
color: #111;
margin-bottom: 15px;
}
.button-box {
height: 44px;
border-top: solid 1px #eee;
flex-direction: row;
align-items: center;
justify-content: space-around;
}
.button-box ,.btn{
height: 44px;
line-height: 44px;
}
.button-box .btn{
flex: 1;
margin: 1px;
text-align: center;
}
.button-box .confirm{
color: #007aff;
border-left: solid 1px #eee;
}
</style>

View File

@ -0,0 +1,83 @@
{
"id": "uni-captcha",
"displayName": "uni-captcha",
"version": "0.7.5",
"description": "云端一体图形验证码组件",
"keywords": [
"captcha",
"图形验证码",
"人机验证",
"防刷",
"防脚本"
],
"repository": "https://gitee.com/dcloud/uni-captcha",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "",
"type": "unicloud-template-function"
},
"uni_modules": {
"dependencies": [
"uni-popup"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "u"
}
}
}
}
}

View File

@ -0,0 +1,3 @@
<h2>
文档已移至 <a href="https://uniapp.dcloud.io/uniCloud/uni-captcha.html" target="_blank">uni-captcha文档</a>
</h2>

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,16 @@
{
"name": "uni-captcha",
"version": "0.7.0",
"description": "uni-captcha",
"main": "index.js",
"homepage": "https://ext.dcloud.net.cn/plugin?id=4048",
"repository": {
"type": "git",
"url": "git+https://gitee.com/dcloud/uni-captcha"
},
"author": "DCloud",
"license": "Apache-2.0",
"dependencies": {
"uni-config-center": "file:../../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center"
}
}

View File

@ -0,0 +1,17 @@
module.exports = {
"image-captcha":{
"width": 150, //图片宽度
"height": 44, //图片高度
"background": "#FFFAE8", //验证码背景色,设置空字符`''`不使用背景颜色
// "size": 4, //验证码长度,最多 6 个字符
// "noise": 4, //验证码干扰线条数
// "color": false, //字体是否使用随机颜色,当设置`background`后恒为`true`
// "fontSize": 40, //字体大小
// "ignoreChars": '', //忽略那些字符
// "mathExpr": false, //是否使用数学表达式
// "mathMin": 1, //表达式所使用的最小数字
// "mathMax": 9, //表达式所使用的最大数字
// "mathOperator": '' //表达式所使用的运算符,支持 `+`、`-`。不传随机使用
// "expiresDate":180 //验证码过期时间(s)
}
}

View File

@ -0,0 +1,35 @@
// 开发文档: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj
//导入验证码公共模块
const uniCaptcha = require('uni-captcha')
//获取数据库对象
const db = uniCloud.database();
//获取数据表opendb-verify-codes对象
const verifyCodes = db.collection('opendb-verify-codes')
module.exports = {
async getImageCaptcha({
scene,isUniAppX
}) {
//获取设备id
let {
deviceId,
platform
} = this.getClientInfo();
//根据设备id、场景值、状态查找记录是否存在
let res = await verifyCodes.where({
scene,
deviceId,
state: 0
}).limit(1).get()
//如果已存在则调用刷新接口,反之调用插件接口
let action = res.data.length ? 'refresh' : 'create'
//执行并返回结果
let option = {
scene, //来源客户端传递,表示:使用场景值,用于防止不同功能的验证码混用
uniPlatform: platform
}
if(isUniAppX){
option.mode = "bmp"
}
return await uniCaptcha[action](option)
}
}

View File

@ -0,0 +1,10 @@
{
"name": "uni-captcha-co",
"dependencies": {
"uni-captcha": "file:../common/uni-captcha",
"uni-config-center": "file:../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center"
},
"extensions": {
"uni-cloud-jql": {}
}
}

View File

@ -0,0 +1,45 @@
{
"bsonType": "object",
"properties": {
"_id": {
"description": "ID系统自动生成"
},
"code": {
"bsonType": "string",
"description": "验证码"
},
"create_date": {
"bsonType": "timestamp",
"description": "创建时间"
},
"device_uuid": {
"bsonType": "string",
"description": "设备UUID常用于图片验证码"
},
"email": {
"bsonType": "string",
"description": "邮箱"
},
"expired_date": {
"bsonType": "timestamp",
"description": "过期时间"
},
"ip": {
"bsonType": "string",
"description": "请求时客户端IP地址"
},
"mobile": {
"bsonType": "string",
"description": "手机号码"
},
"scene": {
"bsonType": "string",
"description": "使用验证码的场景login, bind, unbind, pay"
},
"state": {
"bsonType": "int",
"description": "验证状态0 未验证、1 已验证、2 已作废"
}
},
"required": []
}

View File

@ -0,0 +1,26 @@
## 1.3.12021-12-20
- 修复 在vue页面下略缩图显示不正常的bug
## 1.3.02021-11-19
- 重构插槽的用法 header 替换为 title
- 新增 actions 插槽
- 新增 cover 封面图属性和插槽
- 新增 padding 内容默认内边距离
- 新增 margin 卡片默认外边距离
- 新增 spacing 卡片默认内边距
- 新增 shadow 卡片阴影属性
- 取消 mode 属性,可使用组合插槽代替
- 取消 note 属性 使用actions插槽代替
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-card](https://uniapp.dcloud.io/component/uniui/uni-card)
## 1.2.12021-07-30
- 优化 vue3下事件警告的问题
## 1.2.02021-07-13
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.82021-07-01
- 优化 图文卡片无图片加载时,提供占位图标
- 新增 header 插槽,自定义卡片头部( 图文卡片 mode="style" 时,不支持)
- 修复 thumbnail 不存在仍然占位的 bug
## 1.1.72021-05-12
- 新增 组件示例地址
## 1.1.62021-02-04
- 调整为uni_modules目录规范

View File

@ -0,0 +1,270 @@
<template>
<view class="uni-card" :class="{ 'uni-card--full': isFull, 'uni-card--shadow': isShadow,'uni-card--border':border}"
:style="{'margin':isFull?0:margin,'padding':spacing,'box-shadow':isShadow?shadow:''}">
<!-- 封面 -->
<slot name="cover">
<view v-if="cover" class="uni-card__cover">
<image class="uni-card__cover-image" mode="widthFix" @click="onClick('cover')" :src="cover"></image>
</view>
</slot>
<slot name="title">
<view v-if="title || extra" class="uni-card__header">
<!-- 卡片标题 -->
<view class="uni-card__header-box" @click="onClick('title')">
<view v-if="thumbnail" class="uni-card__header-avatar">
<image class="uni-card__header-avatar-image" :src="thumbnail" mode="aspectFit" />
</view>
<view class="uni-card__header-content">
<text class="uni-card__header-content-title uni-ellipsis">{{ title }}</text>
<text v-if="title&&subTitle"
class="uni-card__header-content-subtitle uni-ellipsis">{{ subTitle }}</text>
</view>
</view>
<view class="uni-card__header-extra" @click="onClick('extra')">
<text class="uni-card__header-extra-text">{{ extra }}</text>
</view>
</view>
</slot>
<!-- 卡片内容 -->
<view class="uni-card__content" :style="{padding:padding}" @click="onClick('content')">
<slot></slot>
</view>
<view class="uni-card__actions" @click="onClick('actions')">
<slot name="actions"></slot>
</view>
</view>
</template>
<script>
/**
* Card 卡片
* @description 卡片视图组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=22
* @property {String} title 标题文字
* @property {String} subTitle 副标题
* @property {Number} padding 内容内边距
* @property {Number} margin 卡片外边距
* @property {Number} spacing 卡片内边距
* @property {String} extra 标题额外信息
* @property {String} cover 封面图(本地路径需要引入)
* @property {String} thumbnail 标题左侧缩略图
* @property {Boolean} is-full = [true | false] 卡片内容是否通栏,为 true 时将去除padding值
* @property {Boolean} is-shadow = [true | false] 卡片内容是否开启阴影
* @property {String} shadow 卡片阴影
* @property {Boolean} border 卡片边框
* @event {Function} click 点击 Card 触发事件
*/
export default {
name: 'UniCard',
emits: ['click'],
props: {
title: {
type: String,
default: ''
},
subTitle: {
type: String,
default: ''
},
padding: {
type: String,
default: '10px'
},
margin: {
type: String,
default: '15px'
},
spacing: {
type: String,
default: '0 10px'
},
extra: {
type: String,
default: ''
},
cover: {
type: String,
default: ''
},
thumbnail: {
type: String,
default: ''
},
isFull: {
// 内容区域是否通栏
type: Boolean,
default: false
},
isShadow: {
// 是否开启阴影
type: Boolean,
default: true
},
shadow: {
type: String,
default: '0px 0px 3px 1px rgba(0, 0, 0, 0.08)'
},
border: {
type: Boolean,
default: true
}
},
methods: {
onClick(type) {
this.$emit('click', type)
}
}
}
</script>
<style lang="scss">
$uni-border-3: #EBEEF5 !default;
$uni-shadow-base:0 0px 6px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default;
$uni-main-color: #3a3a3a !default;
$uni-base-color: #6a6a6a !default;
$uni-secondary-color: #909399 !default;
$uni-spacing-sm: 8px !default;
$uni-border-color:$uni-border-3;
$uni-shadow: $uni-shadow-base;
$uni-card-title: 15px;
$uni-cart-title-color:$uni-main-color;
$uni-card-subtitle: 12px;
$uni-cart-subtitle-color:$uni-secondary-color;
$uni-card-spacing: 10px;
$uni-card-content-color: $uni-base-color;
.uni-card {
margin: $uni-card-spacing;
padding: 0 $uni-spacing-sm;
border-radius: 4px;
overflow: hidden;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, SimSun, sans-serif;
background-color: #fff;
flex: 1;
.uni-card__cover {
position: relative;
margin-top: $uni-card-spacing;
flex-direction: row;
overflow: hidden;
border-radius: 4px;
.uni-card__cover-image {
flex: 1;
// width: 100%;
/* #ifndef APP-PLUS */
vertical-align: middle;
/* #endif */
}
}
.uni-card__header {
display: flex;
border-bottom: 1px $uni-border-color solid;
flex-direction: row;
align-items: center;
padding: $uni-card-spacing;
overflow: hidden;
.uni-card__header-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
overflow: hidden;
}
.uni-card__header-avatar {
width: 40px;
height: 40px;
overflow: hidden;
border-radius: 5px;
margin-right: $uni-card-spacing;
.uni-card__header-avatar-image {
flex: 1;
width: 40px;
height: 40px;
}
}
.uni-card__header-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
flex: 1;
// height: 40px;
overflow: hidden;
.uni-card__header-content-title {
font-size: $uni-card-title;
color: $uni-cart-title-color;
// line-height: 22px;
}
.uni-card__header-content-subtitle {
font-size: $uni-card-subtitle;
margin-top: 5px;
color: $uni-cart-subtitle-color;
}
}
.uni-card__header-extra {
line-height: 12px;
.uni-card__header-extra-text {
font-size: 12px;
color: $uni-cart-subtitle-color;
}
}
}
.uni-card__content {
padding: $uni-card-spacing;
font-size: 14px;
color: $uni-card-content-color;
line-height: 22px;
}
.uni-card__actions {
font-size: 12px;
}
}
.uni-card--border {
border: 1px solid $uni-border-color;
}
.uni-card--shadow {
position: relative;
/* #ifndef APP-NVUE */
box-shadow: $uni-shadow;
/* #endif */
}
.uni-card--full {
margin: 0;
border-left-width: 0;
border-left-width: 0;
border-radius: 0;
}
/* #ifndef APP-NVUE */
.uni-card--full:after {
border-radius: 0;
}
/* #endif */
.uni-ellipsis {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
}
</style>

View File

@ -0,0 +1,90 @@
{
"id": "uni-card",
"displayName": "uni-card 卡片",
"version": "1.3.1",
"description": "Card 组件,提供常见的卡片样式。",
"keywords": [
"uni-ui",
"uniui",
"card",
"",
"卡片"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-icons",
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

View File

@ -0,0 +1,12 @@
## Card 卡片
> **组件名uni-card**
> 代码块: `uCard`
卡片视图组件。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-card)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

View File

@ -0,0 +1,2 @@
## 1.0.12023-03-02
- 修复 方法名错误

View File

@ -0,0 +1,83 @@
{
"id": "uni-cloud-s2s",
"displayName": "服务空间与服务器安全通讯模块",
"version": "1.0.1",
"description": "用于解决服务空间与服务器通讯时互相信任问题",
"keywords": [
"安全通讯",
"服务器请求云函数",
"云函数请求服务器"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "unicloud-template-function",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,3 @@
# uni-cloud-s2s
文档见:[外部服务器如何与uniCloud安全通讯](https://uniapp.dcloud.net.cn/uniCloud/uni-cloud-s2s.html)

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
{
"name": "uni-cloud-s2s",
"version": "1.0.1",
"description": "",
"keywords": [],
"author": "DCloud",
"main": "index.js",
"dependencies": {
"uni-config-center": "file:../../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center"
}
}

View File

@ -0,0 +1,41 @@
## 1.0.162024-06-21
- 修复 小程序发布时无法上传的Bug
## 1.0.152024-06-20
- 修复 小程序访问文章列表报错的Bug
## 1.0.142024-06-12
- 修复 客户端无法显示文章详情问题的Bug
## 1.0.132023-12-09
- 新增 支持uni-app-x需要[uni-cms](https://ext.dcloud.net.cn/plugin?id=11700)插件版本>=1.0.17
## 1.0.122023-10-17
- 修复 使用腾讯云服务空间时无法加载封面图及正文图片的问题
## 1.0.112023-08-07
- 修复 Vue3下因`parse-scen-result.js`文件导出问题导致无法打包编译的bug
## 1.0.102023-07-14
- 新增 文章预览功能,详见[文档](https://uniapp.dcloud.net.cn/uniCloud/uni-cms.html#article-preview)需要uni-cms版本>=1.0.14
- 修复 文章详情页换行重复问题
- 修复 文章列表在数据量小时下拉刷新数据会重复的问题
## 1.0.92023-07-10
- 优化 文章详情页正文可以渲染多个换行
## 1.0.82023-06-21
- 增加 文章列表无图、三图封面样式需要uni-cms版本>=1.0.12
- 优化 文章详情页样式
- 修复 在Vue3下出现 require is not defined 问题
## 1.0.72023-06-07
- 新增 文章详情页面支持播放视频发布视频需要uni-cms版本>=1.0.11
- 优化 文章正文渲染逻辑
- 优化 文章详情页面样式
- 修复 在文章详情页下点击返回按钮无响应问题
## 1.0.62023-04-28
- 修复 文章详情只存在列表时无法渲染的问题
## 1.0.52023-04-24
- 增加 license 文件
## 1.0.42023-04-21
- 优化代码结构,增加代码注释,提高可读性
## 1.0.32023-04-17
- 移除无用schema文件
## 1.0.22023-04-12
- 优化看广告解锁文章交互
## 1.0.12023-04-12
- 优化页面逻辑
## 1.0.02023-04-11
- 插件发布,支持图文内容展示、广告解锁全文功能 [详见文档](https://uniapp.dcloud.net.cn/uniCloud/uni-cms.html)

View File

@ -0,0 +1,68 @@
function parseEditorImage (blocks = []) {
const images = []
if (!Array.isArray(blocks)) {
blocks = [blocks]
}
for (const block of blocks) {
const {insert = {}, attributes = {}} = block
const {'data-custom': custom = ""} = attributes
let parseCustom = custom.split('&').reduce((obj, item) => {
const [key, value] = item.split('=')
if (key && value) {
obj[key] = value
}
return obj
}, {})
images.push({
src: insert.image,
source: parseCustom.source ? parseCustom.source: insert.image
})
}
return images
}
/**
* 解析媒体库/编辑器中的图片
* @param images 图片地址
* @param type {string} 解析类型 media: 媒体库, editor: 编辑器
* @returns {Promise<{src: *, source: *}[]|{src, source: *}[]>}
*/
export async function parseImageUrl (images = [], type = "media") {
if (type === "editor") {
images = parseEditorImage(images).map(item => item.source)
} else {
if (!Array.isArray(images)) {
images = [images]
}
}
if (!images) return null
const tcbFiles = images.filter(item => item.startsWith("cloud://"))
if (tcbFiles.length) {
const res = await uniCloud.getTempFileURL({
fileList: tcbFiles
})
return images.map(image => {
const file = res.fileList.find(item => item.fileID === image)
return {
src: file ? file.tempFileURL : image,
source: image
}
})
} else {
return images.map(image => ({
src: image,
source: image
}))
}
}

View File

@ -0,0 +1,80 @@
export type ParseImageUrlResult = {
src: string
source: string
}
function parseEditorImage (_blocks: any): UTSJSONObject[] {
const images: UTSJSONObject[] = []
let blocks: UTSJSONObject[]
if (!Array.isArray(_blocks)) {
blocks = [_blocks as UTSJSONObject] as UTSJSONObject[]
} else {
blocks = _blocks as UTSJSONObject[]
}
blocks.forEach((block: UTSJSONObject) => {
const insert = block.getJSON('insert')
const attributes = block.getJSON('attributes')
const custom = attributes!.getString('data-custom')
let parseCustom = custom && custom.split('&') ? custom.split('&').reduce((obj: UTSJSONObject, item: string): UTSJSONObject => {
const kv = item.split('=')
if (kv.length > 1) {
obj[kv[0]] = kv[1]
}
return obj
}, {} as UTSJSONObject) : {}
images.push({
src: insert!.getString('image'),
source: parseCustom.getString('source') != null ? parseCustom.getString('source') : insert!.getString('image')
})
})
return images
}
/**
* 解析媒体库/编辑器中的图片
* @param images 图片地址
* @param type {string} 解析类型 media: 媒体库, editor: 编辑器
* @returns {Promise<{src: *, source: *}[]|{src, source: *}[]>}
*/
export async function parseImageUrl (images: any, type: string = "media"): Promise<ParseImageUrlResult[] | null> {
let imagePaths: string[] = []
if (type === "editor") {
imagePaths = parseEditorImage(images).map((item: UTSJSONObject): string => item.getString('source')!)
} else {
if (!Array.isArray(images)) {
imagePaths = [images as string] as string[]
} else {
imagePaths = images
}
}
if (imagePaths.length <= 0) return null
const tcbFiles = imagePaths.filter((item: string): boolean => item.startsWith("cloud://"))
if (tcbFiles.length > 0) {
const res: UniCloudGetTempFileURLResult = await uniCloud.getTempFileURL({
fileList: tcbFiles
})
return imagePaths.map((image: string): ParseImageUrlResult => {
const file = res.fileList.find((item: UniCloudGetTempFileURLResultItem): boolean => item.fileID === image)
return {
src: file ? file.tempFileURL : image,
source: image
} as ParseImageUrlResult
})
} else {
return imagePaths.map((image: string): ParseImageUrlResult => ({
src: image,
source: image
} as ParseImageUrlResult))
}
}

View File

@ -0,0 +1,28 @@
function parseScanResult (scanText) {
const match = scanText.match(/^(.*?):\/\/(.*)/)
if (!match || match.length < 1) {
uni.showToast({
icon: 'none',
title: '未能识别到有效信息'
})
}
const [, protocol, path] = match
switch (protocol) {
case "internallink":
uni.navigateTo({
url: `/${path.replace(/^\//, '')}`,
fail: () => {
uni.showToast({
icon: "none",
title: "访问的路径不存在"
})
}
})
break
}
}
export default parseScanResult

View File

@ -0,0 +1,28 @@
function parseScanResult (scanText: string): void {
const match = scanText.match(/^(.*?):\/\/(.*)/)
if (!match || match.length < 1) {
uni.showToast({
icon: 'none',
title: '未能识别到有效信息'
})
}
const [, protocol, path] = match
switch (protocol) {
case "internallink":
uni.navigateTo({
url: `/${path.replace(/^\//, '')}`,
fail: () => {
uni.showToast({
icon: "none",
title: "访问的路径不存在"
})
}
})
break
}
}
export default parseScanResult

View File

@ -0,0 +1,93 @@
export default function translatePublishTime(timestamp) {
let result = ''
// 获取当前时间
const currentData = new Date()
// 获取发布时间
const date = new Date(timestamp)
// 获取发布年份
const year = date.getFullYear()
// 获取发布月份
const mouth = date.getMonth() + 1
// 获取发布日期
const day = date.getDate()
// 获取发布小时
const hours = date.getHours()
// 获取发布分钟
const minute = date.getMinutes()
// 获取发布秒数
const second = date.getSeconds()
// 获取发布时间戳
const timer = date.getTime()
// 获取当前年份
const currentYear = currentData.getFullYear()
// 获取当前月份
const currentMonth = currentData.getMonth() + 1
// 获取当前日期
const currentDay = currentData.getDate()
// 获取当前小时
const currentHours = currentData.getHours()
// 获取当前分钟
let currentMinute = currentData.getMinutes()
// 获取当前秒数
const currentSecond = currentData.getSeconds()
// 获取当前时间戳
const currentTimer = currentData.getTime()
// 如果时间差小于10秒
if ((currentTimer - timer) < 1000 * 10) {
// 显示刚刚
result = `刚刚`;
// 如果时间差小于60秒
} else if ((currentTimer - timer) < 1000 * 60) {
// 如果当前分钟大于发布分钟
if (currentMinute > minute) {
// 显示秒数差
result = `${(((currentMinute - minute) * 60) + currentSecond - second)}秒前`;
} else {
// 显示秒数差
result = `${(currentSecond - second)}秒前`;
}
// 如果时间差小于1小时
} else if ((currentTimer - timer) < 1000 * (60 * 60)) {
// 如果当前小时大于发布小时
if (currentHours > hours) {
// 显示分钟差
result = `${(((currentHours - hours) * 60) + currentMinute - minute)}分钟前`;
} else {
// 修改 昨天发布的文章时间会出现负数
// 如果当前分钟小于发布分钟
if (currentMinute < minute) {
// 当前分钟加60
currentMinute += 60
}
// 显示分钟差
result = `${(currentMinute - minute)}分钟前`;
}
// 如果时间差小于1天
} else if ((currentTimer - timer) < 1000 * (24 * 60 * 60)) {
// 如果当前日期大于发布日期
if (currentDay > day) {
// 显示小时差
result = `${((currentDay - day) * 24 + currentHours - hours)}小时前`;
} else {
// 修改 跨月-昨天发布的文章时间会出现负数
// 如果当前月份不等于发布月份
if (currentMonth !== mouth) {
// 显示小时差
result = `${(24 + currentHours - hours)}小时前`;
} else {
// 显示小时差
result = `${(currentHours - hours)}小时前`;
}
}
// 如果发布年份等于当前年份
} else if (currentYear === year) {
// 显示月份和日期
result = `${mouth}${day}`;
} else {
// 显示年份、月份和日期
result = `${year}${mouth}${day}`;
}
return result // 返回结果
}

View File

@ -0,0 +1,93 @@
export default function translatePublishTime(timestamp: number): string {
let result: string
// 获取当前时间
const currentData = new Date()
// 获取发布时间
const date = new Date(timestamp)
// 获取发布年份
const year = date.getFullYear()
// 获取发布月份
const mouth = date.getMonth() + 1
// 获取发布日期
const day = date.getDate()
// 获取发布小时
const hours = date.getHours()
// 获取发布分钟
const minute = date.getMinutes()
// 获取发布秒数
const second = date.getSeconds()
// 获取发布时间戳
const timer = date.getTime()
// 获取当前年份
const currentYear = currentData.getFullYear()
// 获取当前月份
const currentMonth = currentData.getMonth() + 1
// 获取当前日期
const currentDay = currentData.getDate()
// 获取当前小时
const currentHours = currentData.getHours()
// 获取当前分钟
let currentMinute = currentData.getMinutes()
// 获取当前秒数
const currentSecond = currentData.getSeconds()
// 获取当前时间戳
const currentTimer = currentData.getTime()
// 如果时间差小于10秒
if ((currentTimer - timer) < 1000 * 10) {
// 显示刚刚
result = `刚刚`;
// 如果时间差小于60秒
} else if ((currentTimer - timer) < 1000 * 60) {
// 如果当前分钟大于发布分钟
if (currentMinute > minute) {
// 显示秒数差
result = `${(((currentMinute - minute) * 60) + currentSecond - second)}秒前`;
} else {
// 显示秒数差
result = `${(currentSecond - second)}秒前`;
}
// 如果时间差小于1小时
} else if ((currentTimer - timer) < 1000 * (60 * 60)) {
// 如果当前小时大于发布小时
if (currentHours > hours) {
// 显示分钟差
result = `${(((currentHours - hours) * 60) + currentMinute - minute)}分钟前`;
} else {
// 修改 昨天发布的文章时间会出现负数
// 如果当前分钟小于发布分钟
if (currentMinute < minute) {
// 当前分钟加60
currentMinute += 60
}
// 显示分钟差
result = `${(currentMinute - minute)}分钟前`;
}
// 如果时间差小于1天
} else if ((currentTimer - timer) < 1000 * (24 * 60 * 60)) {
// 如果当前日期大于发布日期
if (currentDay > day) {
// 显示小时差
result = `${((currentDay - day) * 24 + currentHours - hours)}小时前`;
} else {
// 修改 跨月-昨天发布的文章时间会出现负数
// 如果当前月份不等于发布月份
if (currentMonth !== mouth) {
// 显示小时差
result = `${(24 + currentHours - hours)}小时前`;
} else {
// 显示小时差
result = `${(currentHours - hours)}小时前`;
}
}
// 如果发布年份等于当前年份
} else if (currentYear === year) {
// 显示月份和日期
result = `${mouth}月${day}日`;
} else {
// 显示年份、月份和日期
result = `${year}年${mouth}月${day}日`;
}
return result // 返回结果
}

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