Browse Source
# Conflicts: # subpages/searchResult/pages/resiInfo/resiInfo.js # utils/config.jswork_tab_bar
53 changed files with 14023 additions and 866 deletions
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 91 KiB |
@ -0,0 +1,51 @@ |
|||
import { Int10 } from "./int10"; |
|||
export declare class Stream { |
|||
constructor(enc: Stream | number[], pos?: number); |
|||
private enc; |
|||
pos: number; |
|||
get(pos?: number): number; |
|||
hexDigits: string; |
|||
hexByte(b: number): string; |
|||
hexDump(start: number, end: number, raw: boolean): string; |
|||
isASCII(start: number, end: number): boolean; |
|||
parseStringISO(start: number, end: number): string; |
|||
parseStringUTF(start: number, end: number): string; |
|||
parseStringBMP(start: number, end: number): string; |
|||
parseTime(start: number, end: number, shortYear: boolean): string; |
|||
parseInteger(start: number, end: number): string | 0 | -1; |
|||
parseBitString(start: number, end: number, maxLength: number): string; |
|||
parseOctetString(start: number, end: number, maxLength: number): string; |
|||
parseOID(start: number, end: number, maxLength: number): string; |
|||
} |
|||
export declare class ASN1 { |
|||
constructor(stream: Stream, header: number, length: number, tag: ASN1Tag, sub: ASN1[]); |
|||
private stream; |
|||
private header; |
|||
private length; |
|||
private tag; |
|||
sub: ASN1[]; |
|||
typeName(): string; |
|||
content(maxLength: number): string | 0 | -1; |
|||
toString(): string; |
|||
toPrettyString(indent: string): string; |
|||
posStart(): number; |
|||
posContent(): number; |
|||
posEnd(): number; |
|||
toHexString(): string; |
|||
static decodeLength(stream: Stream): number; |
|||
/** |
|||
* Retrieve the hexadecimal value (as a string) of the current ASN.1 element |
|||
* @returns {string} |
|||
* @public |
|||
*/ |
|||
getHexStringValue(): string; |
|||
static decode(str: Stream | number[]): ASN1; |
|||
} |
|||
export declare class ASN1Tag { |
|||
constructor(stream: Stream); |
|||
tagClass: number; |
|||
tagConstructed: boolean; |
|||
tagNumber: number | Int10; |
|||
isUniversal(): boolean; |
|||
isEOC(): boolean; |
|||
} |
|||
@ -0,0 +1,565 @@ |
|||
// ASN.1 JavaScript decoder
|
|||
// Copyright (c) 2008-2014 Lapo Luchini <lapo@lapo.it>
|
|||
// Permission to use, copy, modify, and/or distribute this software for any
|
|||
// purpose with or without fee is hereby granted, provided that the above
|
|||
// copyright notice and this permission notice appear in all copies.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ |
|||
/*global oids */ |
|||
import { Int10 } from "./int10"; |
|||
var ellipsis = "\u2026"; |
|||
var reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; |
|||
var reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; |
|||
function stringCut(str, len) { |
|||
if (str.length > len) { |
|||
str = str.substring(0, len) + ellipsis; |
|||
} |
|||
return str; |
|||
} |
|||
var Stream = /** @class */ (function () { |
|||
function Stream(enc, pos) { |
|||
this.hexDigits = "0123456789ABCDEF"; |
|||
if (enc instanceof Stream) { |
|||
this.enc = enc.enc; |
|||
this.pos = enc.pos; |
|||
} |
|||
else { |
|||
// enc should be an array or a binary string
|
|||
this.enc = enc; |
|||
this.pos = pos; |
|||
} |
|||
} |
|||
Stream.prototype.get = function (pos) { |
|||
if (pos === undefined) { |
|||
pos = this.pos++; |
|||
} |
|||
if (pos >= this.enc.length) { |
|||
throw new Error("Requesting byte offset ".concat(pos, " on a stream of length ").concat(this.enc.length)); |
|||
} |
|||
return ("string" === typeof this.enc) ? this.enc.charCodeAt(pos) : this.enc[pos]; |
|||
}; |
|||
Stream.prototype.hexByte = function (b) { |
|||
return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF); |
|||
}; |
|||
Stream.prototype.hexDump = function (start, end, raw) { |
|||
var s = ""; |
|||
for (var i = start; i < end; ++i) { |
|||
s += this.hexByte(this.get(i)); |
|||
if (raw !== true) { |
|||
switch (i & 0xF) { |
|||
case 0x7: |
|||
s += " "; |
|||
break; |
|||
case 0xF: |
|||
s += "\n"; |
|||
break; |
|||
default: |
|||
s += " "; |
|||
} |
|||
} |
|||
} |
|||
return s; |
|||
}; |
|||
Stream.prototype.isASCII = function (start, end) { |
|||
for (var i = start; i < end; ++i) { |
|||
var c = this.get(i); |
|||
if (c < 32 || c > 176) { |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
}; |
|||
Stream.prototype.parseStringISO = function (start, end) { |
|||
var s = ""; |
|||
for (var i = start; i < end; ++i) { |
|||
s += String.fromCharCode(this.get(i)); |
|||
} |
|||
return s; |
|||
}; |
|||
Stream.prototype.parseStringUTF = function (start, end) { |
|||
var s = ""; |
|||
for (var i = start; i < end;) { |
|||
var c = this.get(i++); |
|||
if (c < 128) { |
|||
s += String.fromCharCode(c); |
|||
} |
|||
else if ((c > 191) && (c < 224)) { |
|||
s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); |
|||
} |
|||
else { |
|||
s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); |
|||
} |
|||
} |
|||
return s; |
|||
}; |
|||
Stream.prototype.parseStringBMP = function (start, end) { |
|||
var str = ""; |
|||
var hi; |
|||
var lo; |
|||
for (var i = start; i < end;) { |
|||
hi = this.get(i++); |
|||
lo = this.get(i++); |
|||
str += String.fromCharCode((hi << 8) | lo); |
|||
} |
|||
return str; |
|||
}; |
|||
Stream.prototype.parseTime = function (start, end, shortYear) { |
|||
var s = this.parseStringISO(start, end); |
|||
var m = (shortYear ? reTimeS : reTimeL).exec(s); |
|||
if (!m) { |
|||
return "Unrecognized time: " + s; |
|||
} |
|||
if (shortYear) { |
|||
// to avoid querying the timer, use the fixed range [1970, 2069]
|
|||
// it will conform with ITU X.400 [-10, +40] sliding window until 2030
|
|||
m[1] = +m[1]; |
|||
m[1] += (+m[1] < 70) ? 2000 : 1900; |
|||
} |
|||
s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; |
|||
if (m[5]) { |
|||
s += ":" + m[5]; |
|||
if (m[6]) { |
|||
s += ":" + m[6]; |
|||
if (m[7]) { |
|||
s += "." + m[7]; |
|||
} |
|||
} |
|||
} |
|||
if (m[8]) { |
|||
s += " UTC"; |
|||
if (m[8] != "Z") { |
|||
s += m[8]; |
|||
if (m[9]) { |
|||
s += ":" + m[9]; |
|||
} |
|||
} |
|||
} |
|||
return s; |
|||
}; |
|||
Stream.prototype.parseInteger = function (start, end) { |
|||
var v = this.get(start); |
|||
var neg = (v > 127); |
|||
var pad = neg ? 255 : 0; |
|||
var len; |
|||
var s = ""; |
|||
// skip unuseful bits (not allowed in DER)
|
|||
while (v == pad && ++start < end) { |
|||
v = this.get(start); |
|||
} |
|||
len = end - start; |
|||
if (len === 0) { |
|||
return neg ? -1 : 0; |
|||
} |
|||
// show bit length of huge integers
|
|||
if (len > 4) { |
|||
s = v; |
|||
len <<= 3; |
|||
while (((+s ^ pad) & 0x80) == 0) { |
|||
s = +s << 1; |
|||
--len; |
|||
} |
|||
s = "(" + len + " bit)\n"; |
|||
} |
|||
// decode the integer
|
|||
if (neg) { |
|||
v = v - 256; |
|||
} |
|||
var n = new Int10(v); |
|||
for (var i = start + 1; i < end; ++i) { |
|||
n.mulAdd(256, this.get(i)); |
|||
} |
|||
return s + n.toString(); |
|||
}; |
|||
Stream.prototype.parseBitString = function (start, end, maxLength) { |
|||
var unusedBit = this.get(start); |
|||
var lenBit = ((end - start - 1) << 3) - unusedBit; |
|||
var intro = "(" + lenBit + " bit)\n"; |
|||
var s = ""; |
|||
for (var i = start + 1; i < end; ++i) { |
|||
var b = this.get(i); |
|||
var skip = (i == end - 1) ? unusedBit : 0; |
|||
for (var j = 7; j >= skip; --j) { |
|||
s += (b >> j) & 1 ? "1" : "0"; |
|||
} |
|||
if (s.length > maxLength) { |
|||
return intro + stringCut(s, maxLength); |
|||
} |
|||
} |
|||
return intro + s; |
|||
}; |
|||
Stream.prototype.parseOctetString = function (start, end, maxLength) { |
|||
if (this.isASCII(start, end)) { |
|||
return stringCut(this.parseStringISO(start, end), maxLength); |
|||
} |
|||
var len = end - start; |
|||
var s = "(" + len + " byte)\n"; |
|||
maxLength /= 2; // we work in bytes
|
|||
if (len > maxLength) { |
|||
end = start + maxLength; |
|||
} |
|||
for (var i = start; i < end; ++i) { |
|||
s += this.hexByte(this.get(i)); |
|||
} |
|||
if (len > maxLength) { |
|||
s += ellipsis; |
|||
} |
|||
return s; |
|||
}; |
|||
Stream.prototype.parseOID = function (start, end, maxLength) { |
|||
var s = ""; |
|||
var n = new Int10(); |
|||
var bits = 0; |
|||
for (var i = start; i < end; ++i) { |
|||
var v = this.get(i); |
|||
n.mulAdd(128, v & 0x7F); |
|||
bits += 7; |
|||
if (!(v & 0x80)) { // finished
|
|||
if (s === "") { |
|||
n = n.simplify(); |
|||
if (n instanceof Int10) { |
|||
n.sub(80); |
|||
s = "2." + n.toString(); |
|||
} |
|||
else { |
|||
var m = n < 80 ? n < 40 ? 0 : 1 : 2; |
|||
s = m + "." + (n - m * 40); |
|||
} |
|||
} |
|||
else { |
|||
s += "." + n.toString(); |
|||
} |
|||
if (s.length > maxLength) { |
|||
return stringCut(s, maxLength); |
|||
} |
|||
n = new Int10(); |
|||
bits = 0; |
|||
} |
|||
} |
|||
if (bits > 0) { |
|||
s += ".incomplete"; |
|||
} |
|||
return s; |
|||
}; |
|||
return Stream; |
|||
}()); |
|||
export { Stream }; |
|||
var ASN1 = /** @class */ (function () { |
|||
function ASN1(stream, header, length, tag, sub) { |
|||
if (!(tag instanceof ASN1Tag)) { |
|||
throw new Error("Invalid tag value."); |
|||
} |
|||
this.stream = stream; |
|||
this.header = header; |
|||
this.length = length; |
|||
this.tag = tag; |
|||
this.sub = sub; |
|||
} |
|||
ASN1.prototype.typeName = function () { |
|||
switch (this.tag.tagClass) { |
|||
case 0: // universal
|
|||
switch (this.tag.tagNumber) { |
|||
case 0x00: |
|||
return "EOC"; |
|||
case 0x01: |
|||
return "BOOLEAN"; |
|||
case 0x02: |
|||
return "INTEGER"; |
|||
case 0x03: |
|||
return "BIT_STRING"; |
|||
case 0x04: |
|||
return "OCTET_STRING"; |
|||
case 0x05: |
|||
return "NULL"; |
|||
case 0x06: |
|||
return "OBJECT_IDENTIFIER"; |
|||
case 0x07: |
|||
return "ObjectDescriptor"; |
|||
case 0x08: |
|||
return "EXTERNAL"; |
|||
case 0x09: |
|||
return "REAL"; |
|||
case 0x0A: |
|||
return "ENUMERATED"; |
|||
case 0x0B: |
|||
return "EMBEDDED_PDV"; |
|||
case 0x0C: |
|||
return "UTF8String"; |
|||
case 0x10: |
|||
return "SEQUENCE"; |
|||
case 0x11: |
|||
return "SET"; |
|||
case 0x12: |
|||
return "NumericString"; |
|||
case 0x13: |
|||
return "PrintableString"; // ASCII subset
|
|||
case 0x14: |
|||
return "TeletexString"; // aka T61String
|
|||
case 0x15: |
|||
return "VideotexString"; |
|||
case 0x16: |
|||
return "IA5String"; // ASCII
|
|||
case 0x17: |
|||
return "UTCTime"; |
|||
case 0x18: |
|||
return "GeneralizedTime"; |
|||
case 0x19: |
|||
return "GraphicString"; |
|||
case 0x1A: |
|||
return "VisibleString"; // ASCII subset
|
|||
case 0x1B: |
|||
return "GeneralString"; |
|||
case 0x1C: |
|||
return "UniversalString"; |
|||
case 0x1E: |
|||
return "BMPString"; |
|||
} |
|||
return "Universal_" + this.tag.tagNumber.toString(); |
|||
case 1: |
|||
return "Application_" + this.tag.tagNumber.toString(); |
|||
case 2: |
|||
return "[" + this.tag.tagNumber.toString() + "]"; // Context
|
|||
case 3: |
|||
return "Private_" + this.tag.tagNumber.toString(); |
|||
} |
|||
}; |
|||
ASN1.prototype.content = function (maxLength) { |
|||
if (this.tag === undefined) { |
|||
return null; |
|||
} |
|||
if (maxLength === undefined) { |
|||
maxLength = Infinity; |
|||
} |
|||
var content = this.posContent(); |
|||
var len = Math.abs(this.length); |
|||
if (!this.tag.isUniversal()) { |
|||
if (this.sub !== null) { |
|||
return "(" + this.sub.length + " elem)"; |
|||
} |
|||
return this.stream.parseOctetString(content, content + len, maxLength); |
|||
} |
|||
switch (this.tag.tagNumber) { |
|||
case 0x01: // BOOLEAN
|
|||
return (this.stream.get(content) === 0) ? "false" : "true"; |
|||
case 0x02: // INTEGER
|
|||
return this.stream.parseInteger(content, content + len); |
|||
case 0x03: // BIT_STRING
|
|||
return this.sub ? "(" + this.sub.length + " elem)" : |
|||
this.stream.parseBitString(content, content + len, maxLength); |
|||
case 0x04: // OCTET_STRING
|
|||
return this.sub ? "(" + this.sub.length + " elem)" : |
|||
this.stream.parseOctetString(content, content + len, maxLength); |
|||
// case 0x05: // NULL
|
|||
case 0x06: // OBJECT_IDENTIFIER
|
|||
return this.stream.parseOID(content, content + len, maxLength); |
|||
// case 0x07: // ObjectDescriptor
|
|||
// case 0x08: // EXTERNAL
|
|||
// case 0x09: // REAL
|
|||
// case 0x0A: // ENUMERATED
|
|||
// case 0x0B: // EMBEDDED_PDV
|
|||
case 0x10: // SEQUENCE
|
|||
case 0x11: // SET
|
|||
if (this.sub !== null) { |
|||
return "(" + this.sub.length + " elem)"; |
|||
} |
|||
else { |
|||
return "(no elem)"; |
|||
} |
|||
case 0x0C: // UTF8String
|
|||
return stringCut(this.stream.parseStringUTF(content, content + len), maxLength); |
|||
case 0x12: // NumericString
|
|||
case 0x13: // PrintableString
|
|||
case 0x14: // TeletexString
|
|||
case 0x15: // VideotexString
|
|||
case 0x16: // IA5String
|
|||
// case 0x19: // GraphicString
|
|||
case 0x1A: // VisibleString
|
|||
// case 0x1B: // GeneralString
|
|||
// case 0x1C: // UniversalString
|
|||
return stringCut(this.stream.parseStringISO(content, content + len), maxLength); |
|||
case 0x1E: // BMPString
|
|||
return stringCut(this.stream.parseStringBMP(content, content + len), maxLength); |
|||
case 0x17: // UTCTime
|
|||
case 0x18: // GeneralizedTime
|
|||
return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); |
|||
} |
|||
return null; |
|||
}; |
|||
ASN1.prototype.toString = function () { |
|||
return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? "null" : this.sub.length) + "]"; |
|||
}; |
|||
ASN1.prototype.toPrettyString = function (indent) { |
|||
if (indent === undefined) { |
|||
indent = ""; |
|||
} |
|||
var s = indent + this.typeName() + " @" + this.stream.pos; |
|||
if (this.length >= 0) { |
|||
s += "+"; |
|||
} |
|||
s += this.length; |
|||
if (this.tag.tagConstructed) { |
|||
s += " (constructed)"; |
|||
} |
|||
else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) { |
|||
s += " (encapsulates)"; |
|||
} |
|||
s += "\n"; |
|||
if (this.sub !== null) { |
|||
indent += " "; |
|||
for (var i = 0, max = this.sub.length; i < max; ++i) { |
|||
s += this.sub[i].toPrettyString(indent); |
|||
} |
|||
} |
|||
return s; |
|||
}; |
|||
ASN1.prototype.posStart = function () { |
|||
return this.stream.pos; |
|||
}; |
|||
ASN1.prototype.posContent = function () { |
|||
return this.stream.pos + this.header; |
|||
}; |
|||
ASN1.prototype.posEnd = function () { |
|||
return this.stream.pos + this.header + Math.abs(this.length); |
|||
}; |
|||
ASN1.prototype.toHexString = function () { |
|||
return this.stream.hexDump(this.posStart(), this.posEnd(), true); |
|||
}; |
|||
ASN1.decodeLength = function (stream) { |
|||
var buf = stream.get(); |
|||
var len = buf & 0x7F; |
|||
if (len == buf) { |
|||
return len; |
|||
} |
|||
// no reason to use Int10, as it would be a huge buffer anyways
|
|||
if (len > 6) { |
|||
throw new Error("Length over 48 bits not supported at position " + (stream.pos - 1)); |
|||
} |
|||
if (len === 0) { |
|||
return null; |
|||
} // undefined
|
|||
buf = 0; |
|||
for (var i = 0; i < len; ++i) { |
|||
buf = (buf * 256) + stream.get(); |
|||
} |
|||
return buf; |
|||
}; |
|||
/** |
|||
* Retrieve the hexadecimal value (as a string) of the current ASN.1 element |
|||
* @returns {string} |
|||
* @public |
|||
*/ |
|||
ASN1.prototype.getHexStringValue = function () { |
|||
var hexString = this.toHexString(); |
|||
var offset = this.header * 2; |
|||
var length = this.length * 2; |
|||
return hexString.substr(offset, length); |
|||
}; |
|||
ASN1.decode = function (str) { |
|||
var stream; |
|||
if (!(str instanceof Stream)) { |
|||
stream = new Stream(str, 0); |
|||
} |
|||
else { |
|||
stream = str; |
|||
} |
|||
var streamStart = new Stream(stream); |
|||
var tag = new ASN1Tag(stream); |
|||
var len = ASN1.decodeLength(stream); |
|||
var start = stream.pos; |
|||
var header = start - streamStart.pos; |
|||
var sub = null; |
|||
var getSub = function () { |
|||
var ret = []; |
|||
if (len !== null) { |
|||
// definite length
|
|||
var end = start + len; |
|||
while (stream.pos < end) { |
|||
ret[ret.length] = ASN1.decode(stream); |
|||
} |
|||
if (stream.pos != end) { |
|||
throw new Error("Content size is not correct for container starting at offset " + start); |
|||
} |
|||
} |
|||
else { |
|||
// undefined length
|
|||
try { |
|||
for (;;) { |
|||
var s = ASN1.decode(stream); |
|||
if (s.tag.isEOC()) { |
|||
break; |
|||
} |
|||
ret[ret.length] = s; |
|||
} |
|||
len = start - stream.pos; // undefined lengths are represented as negative values
|
|||
} |
|||
catch (e) { |
|||
throw new Error("Exception while decoding undefined length content: " + e); |
|||
} |
|||
} |
|||
return ret; |
|||
}; |
|||
if (tag.tagConstructed) { |
|||
// must have valid content
|
|||
sub = getSub(); |
|||
} |
|||
else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { |
|||
// sometimes BitString and OctetString are used to encapsulate ASN.1
|
|||
try { |
|||
if (tag.tagNumber == 0x03) { |
|||
if (stream.get() != 0) { |
|||
throw new Error("BIT STRINGs with unused bits cannot encapsulate."); |
|||
} |
|||
} |
|||
sub = getSub(); |
|||
for (var i = 0; i < sub.length; ++i) { |
|||
if (sub[i].tag.isEOC()) { |
|||
throw new Error("EOC is not supposed to be actual content."); |
|||
} |
|||
} |
|||
} |
|||
catch (e) { |
|||
// but silently ignore when they don't
|
|||
sub = null; |
|||
} |
|||
} |
|||
if (sub === null) { |
|||
if (len === null) { |
|||
throw new Error("We can't skip over an invalid tag with undefined length at offset " + start); |
|||
} |
|||
stream.pos = start + Math.abs(len); |
|||
} |
|||
return new ASN1(streamStart, header, len, tag, sub); |
|||
}; |
|||
return ASN1; |
|||
}()); |
|||
export { ASN1 }; |
|||
var ASN1Tag = /** @class */ (function () { |
|||
function ASN1Tag(stream) { |
|||
var buf = stream.get(); |
|||
this.tagClass = buf >> 6; |
|||
this.tagConstructed = ((buf & 0x20) !== 0); |
|||
this.tagNumber = buf & 0x1F; |
|||
if (this.tagNumber == 0x1F) { // long tag
|
|||
var n = new Int10(); |
|||
do { |
|||
buf = stream.get(); |
|||
n.mulAdd(128, buf & 0x7F); |
|||
} while (buf & 0x80); |
|||
this.tagNumber = n.simplify(); |
|||
} |
|||
} |
|||
ASN1Tag.prototype.isUniversal = function () { |
|||
return this.tagClass === 0x00; |
|||
}; |
|||
ASN1Tag.prototype.isEOC = function () { |
|||
return this.tagClass === 0x00 && this.tagNumber === 0x00; |
|||
}; |
|||
return ASN1Tag; |
|||
}()); |
|||
export { ASN1Tag }; |
|||
@ -0,0 +1,5 @@ |
|||
export declare const Base64: { |
|||
decode(a: string): number[]; |
|||
re: RegExp; |
|||
unarmor(a: string): number[]; |
|||
}; |
|||
@ -0,0 +1,88 @@ |
|||
// Base64 JavaScript decoder
|
|||
// Copyright (c) 2008-2013 Lapo Luchini <lapo@lapo.it>
|
|||
// Permission to use, copy, modify, and/or distribute this software for any
|
|||
// purpose with or without fee is hereby granted, provided that the above
|
|||
// copyright notice and this permission notice appear in all copies.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ |
|||
var decoder; |
|||
export var Base64 = { |
|||
decode: function (a) { |
|||
var i; |
|||
if (decoder === undefined) { |
|||
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|||
var ignore = "= \f\n\r\t\u00A0\u2028\u2029"; |
|||
decoder = Object.create(null); |
|||
for (i = 0; i < 64; ++i) { |
|||
decoder[b64.charAt(i)] = i; |
|||
} |
|||
decoder['-'] = 62; //+
|
|||
decoder['_'] = 63; //-
|
|||
for (i = 0; i < ignore.length; ++i) { |
|||
decoder[ignore.charAt(i)] = -1; |
|||
} |
|||
} |
|||
var out = []; |
|||
var bits = 0; |
|||
var char_count = 0; |
|||
for (i = 0; i < a.length; ++i) { |
|||
var c = a.charAt(i); |
|||
if (c == "=") { |
|||
break; |
|||
} |
|||
c = decoder[c]; |
|||
if (c == -1) { |
|||
continue; |
|||
} |
|||
if (c === undefined) { |
|||
throw new Error("Illegal character at offset " + i); |
|||
} |
|||
bits |= c; |
|||
if (++char_count >= 4) { |
|||
out[out.length] = (bits >> 16); |
|||
out[out.length] = (bits >> 8) & 0xFF; |
|||
out[out.length] = bits & 0xFF; |
|||
bits = 0; |
|||
char_count = 0; |
|||
} |
|||
else { |
|||
bits <<= 6; |
|||
} |
|||
} |
|||
switch (char_count) { |
|||
case 1: |
|||
throw new Error("Base64 encoding incomplete: at least 2 bits missing"); |
|||
case 2: |
|||
out[out.length] = (bits >> 10); |
|||
break; |
|||
case 3: |
|||
out[out.length] = (bits >> 16); |
|||
out[out.length] = (bits >> 8) & 0xFF; |
|||
break; |
|||
} |
|||
return out; |
|||
}, |
|||
re: /-----BEGIN [^-]+-----([A-Za-z0-9+\/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/, |
|||
unarmor: function (a) { |
|||
var m = Base64.re.exec(a); |
|||
if (m) { |
|||
if (m[1]) { |
|||
a = m[1]; |
|||
} |
|||
else if (m[2]) { |
|||
a = m[2]; |
|||
} |
|||
else { |
|||
throw new Error("RegExp out of sync"); |
|||
} |
|||
} |
|||
return Base64.decode(a); |
|||
} |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export declare const Hex: { |
|||
decode(a: string): number[]; |
|||
}; |
|||
@ -0,0 +1,64 @@ |
|||
// Hex JavaScript decoder
|
|||
// Copyright (c) 2008-2013 Lapo Luchini <lapo@lapo.it>
|
|||
// Permission to use, copy, modify, and/or distribute this software for any
|
|||
// purpose with or without fee is hereby granted, provided that the above
|
|||
// copyright notice and this permission notice appear in all copies.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ |
|||
var decoder; |
|||
export var Hex = { |
|||
decode: function (a) { |
|||
var i; |
|||
if (decoder === undefined) { |
|||
var hex = "0123456789ABCDEF"; |
|||
var ignore = " \f\n\r\t\u00A0\u2028\u2029"; |
|||
decoder = {}; |
|||
for (i = 0; i < 16; ++i) { |
|||
decoder[hex.charAt(i)] = i; |
|||
} |
|||
hex = hex.toLowerCase(); |
|||
for (i = 10; i < 16; ++i) { |
|||
decoder[hex.charAt(i)] = i; |
|||
} |
|||
for (i = 0; i < ignore.length; ++i) { |
|||
decoder[ignore.charAt(i)] = -1; |
|||
} |
|||
} |
|||
var out = []; |
|||
var bits = 0; |
|||
var char_count = 0; |
|||
for (i = 0; i < a.length; ++i) { |
|||
var c = a.charAt(i); |
|||
if (c == "=") { |
|||
break; |
|||
} |
|||
c = decoder[c]; |
|||
if (c == -1) { |
|||
continue; |
|||
} |
|||
if (c === undefined) { |
|||
throw new Error("Illegal character at offset " + i); |
|||
} |
|||
bits |= c; |
|||
if (++char_count >= 2) { |
|||
out[out.length] = bits; |
|||
bits = 0; |
|||
char_count = 0; |
|||
} |
|||
else { |
|||
bits <<= 4; |
|||
} |
|||
} |
|||
if (char_count) { |
|||
throw new Error("Hex encoding incomplete: 4 bits missing"); |
|||
} |
|||
return out; |
|||
} |
|||
}; |
|||
@ -0,0 +1,9 @@ |
|||
export declare class Int10 { |
|||
constructor(value?: string | number); |
|||
mulAdd(m: number, c: number): void; |
|||
sub(c: number): void; |
|||
toString(base?: number): string; |
|||
valueOf(): number; |
|||
simplify(): number | this; |
|||
private buf; |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
// Big integer base-10 printing library
|
|||
// Copyright (c) 2014 Lapo Luchini <lapo@lapo.it>
|
|||
// Permission to use, copy, modify, and/or distribute this software for any
|
|||
// purpose with or without fee is hereby granted, provided that the above
|
|||
// copyright notice and this permission notice appear in all copies.
|
|||
//
|
|||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
/*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ |
|||
var max = 10000000000000; // biggest integer that can still fit 2^53 when multiplied by 256
|
|||
var Int10 = /** @class */ (function () { |
|||
function Int10(value) { |
|||
this.buf = [+value || 0]; |
|||
} |
|||
Int10.prototype.mulAdd = function (m, c) { |
|||
// assert(m <= 256)
|
|||
var b = this.buf; |
|||
var l = b.length; |
|||
var i; |
|||
var t; |
|||
for (i = 0; i < l; ++i) { |
|||
t = b[i] * m + c; |
|||
if (t < max) { |
|||
c = 0; |
|||
} |
|||
else { |
|||
c = 0 | (t / max); |
|||
t -= c * max; |
|||
} |
|||
b[i] = t; |
|||
} |
|||
if (c > 0) { |
|||
b[i] = c; |
|||
} |
|||
}; |
|||
Int10.prototype.sub = function (c) { |
|||
// assert(m <= 256)
|
|||
var b = this.buf; |
|||
var l = b.length; |
|||
var i; |
|||
var t; |
|||
for (i = 0; i < l; ++i) { |
|||
t = b[i] - c; |
|||
if (t < 0) { |
|||
t += max; |
|||
c = 1; |
|||
} |
|||
else { |
|||
c = 0; |
|||
} |
|||
b[i] = t; |
|||
} |
|||
while (b[b.length - 1] === 0) { |
|||
b.pop(); |
|||
} |
|||
}; |
|||
Int10.prototype.toString = function (base) { |
|||
if ((base || 10) != 10) { |
|||
throw new Error("only base 10 is supported"); |
|||
} |
|||
var b = this.buf; |
|||
var s = b[b.length - 1].toString(); |
|||
for (var i = b.length - 2; i >= 0; --i) { |
|||
s += (max + b[i]).toString().substring(1); |
|||
} |
|||
return s; |
|||
}; |
|||
Int10.prototype.valueOf = function () { |
|||
var b = this.buf; |
|||
var v = 0; |
|||
for (var i = b.length - 1; i >= 0; --i) { |
|||
v = v * max + b[i]; |
|||
} |
|||
return v; |
|||
}; |
|||
Int10.prototype.simplify = function () { |
|||
var b = this.buf; |
|||
return (b.length == 1) ? b[0] : this; |
|||
}; |
|||
return Int10; |
|||
}()); |
|||
export { Int10 }; |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,3 +1,6 @@ |
|||
{ |
|||
"usingComponents": {} |
|||
"component": true, |
|||
"usingComponents": { |
|||
"no-data": "../../../components/noData/nodata" |
|||
} |
|||
} |
|||
@ -1,23 +1,43 @@ |
|||
<view class="tag-list"> |
|||
<view wx:for="{{tabList}}" |
|||
class="tag {{item.value === tabValue?'cur':''}}" |
|||
wx:key="index" |
|||
bind:tap="tabChange" |
|||
data-index="{{item.value}}"> |
|||
{{item.label}} |
|||
<view wx:for="{{tabList}}" class="tag {{item.value === tabValue?'cur':''}}" wx:key="index" bind:tap="tabChange" data-index="{{item.value}}"> |
|||
{{item.label}}({{ywData[item.value].length}}) |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="table"> |
|||
<view class="row header"> |
|||
<view class="cell">事件类型</view> |
|||
<view class="cell">事件描述</view> |
|||
</view> |
|||
<!-- <block wx:for="{{data[tabValue]}}" wx:for-item="item" wx:for-index="index"> --> |
|||
<block bind:tap="gotopage" data-obj="{{item}}" wx:for="{{data[tabValue]}}" wx:key="index"> |
|||
<view class="row {{index % 2 == 0 ? 'even' : 'odd'}}"> |
|||
<view class="cell">{{item.categorycode}}</view> |
|||
<view class="cell">{{item.eventcontent}}</view> |
|||
<view wx:if="{{tabValue === '12345'||tabValue === 'event'}}" class="row header"> |
|||
<view class="cell">事件类型</view> |
|||
<view class="cell">事件描述</view> |
|||
</view> |
|||
</block> |
|||
</view> |
|||
<view wx:if="{{tabValue === 'provinceSurvey'||tabValue === 'selfSurvey'}}" class="row header"> |
|||
<view class="cell">不满意事项类型</view> |
|||
<view class="cell">不满意事项描述</view> |
|||
</view> |
|||
<view wx:if="{{tabValue === 'need'}}" class="row header"> |
|||
<view class="cell">需求类型</view> |
|||
<view class="cell">需求描述</view> |
|||
</view> |
|||
<view wx:if="{{tabValue === 'serve'}}" class="row header"> |
|||
<view class="cell">需求类型</view> |
|||
<view class="cell">事件描述</view> |
|||
</view> |
|||
<view wx:if="{{tabValue === 'punchRecord'}}" class="row header"> |
|||
<view class="cell1">打卡人</view> |
|||
<view class="cell1">打卡地址</view> |
|||
<view class="cell1">打卡时间</view> |
|||
</view> |
|||
<block bind:tap="gotopage" wx:if="{{tabValue !== 'punchRecord'}}" data-obj="{{item}}" wx:for="{{ywData[tabValue]}}" wx:key="index"> |
|||
<view class="row {{index % 2 == 0 ? 'even' : 'odd'}}"> |
|||
<view class="cell">{{item.categorycode}}</view> |
|||
<view class="cell">{{item.eventcontent}}</view> |
|||
</view> |
|||
</block> |
|||
<block bind:tap="gotopage" wx:if="{{tabValue == 'punchRecord'}}" data-obj="{{item}}" wx:for="{{ywData[tabValue]}}" wx:key="index"> |
|||
<view class="row {{index % 2 == 0 ? 'even' : 'odd'}}"> |
|||
<view class="cell1">{{item.name}}</view> |
|||
<view class="cell1">{{item.clockInAddress}}</view> |
|||
<view class="cell1">{{item.clockInTime}}</view> |
|||
</view> |
|||
</block> |
|||
<no-data isShow="{{ywData[tabValue].length===0}}" wx:if="{{ywData[tabValue].length===0}}"></no-data> |
|||
</view> |
|||
@ -0,0 +1,189 @@ |
|||
import * as echarts from '../../../../../../ec-canvas/echarts'; |
|||
import { |
|||
getFamilyRelationshipList |
|||
} from "../../../../../../utils/api"; |
|||
var chart; |
|||
|
|||
const formatNumber = n => { |
|||
n = n.toString() |
|||
return n[1] ? n : '0' + n |
|||
} |
|||
Component({ |
|||
properties: { |
|||
houseId: { |
|||
type: String, |
|||
value: '', |
|||
}, |
|||
houseName:{ |
|||
type: String, |
|||
value: '', |
|||
} |
|||
}, |
|||
data: { |
|||
ec: { |
|||
lazyLoad: true |
|||
// onInit: initChart
|
|||
}, |
|||
tabList: [{ |
|||
label: "问题突出类别", |
|||
value: 1 |
|||
}, { |
|||
label: '行业领域分析', |
|||
value: 2 |
|||
}], |
|||
tabValue: 1 |
|||
}, |
|||
lifetimes: { |
|||
ready() { |
|||
console.log(this.data.houseName,'houseName'); |
|||
this.getData() |
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
getData() { |
|||
this.selectComponent('#hotlineComplaints').init((canvas, width, height, dpr) => { |
|||
chart = echarts.init(canvas, null, { |
|||
width: width, |
|||
height: height, |
|||
devicePixelRatio: dpr // 像素比
|
|||
}); |
|||
canvas.setChart(chart); |
|||
let params = { |
|||
type: "1", |
|||
id: this.data.houseId |
|||
} |
|||
|
|||
getFamilyRelationshipList(params).then(({ |
|||
data |
|||
}) => { |
|||
// let index = data.findIndex(item => item.id == this.data.houseId);
|
|||
// if (index !== -1) {
|
|||
// const [item] = data.splice(index, 1);
|
|||
// data.unshift(item);
|
|||
// }
|
|||
|
|||
const rootNode = {}; |
|||
rootNode["id"] = this.data.houseId; |
|||
rootNode["name"] = this.data.houseName; |
|||
rootNode["type"] = 1; |
|||
data.unshift(rootNode); |
|||
let nodeList = data.map((item, index) => { |
|||
let symbolUrl = ''; |
|||
if (item.type === 1) { |
|||
symbolUrl = 'image://https://elink-esua-epdc.oss-cn-qingdao.aliyuncs.com/epmet/test/internal/20240402/a6cdfe6f699040f58fd8b2c10486b41d.png'; |
|||
} else { |
|||
// 如果 type 不等于 1,则根据 gender 进行判断
|
|||
if (item.gender === '1') { |
|||
// 如果 gender 等于 1,则根据 age 进行进一步的判断
|
|||
symbolUrl = item.age > 18 && item.age < 60 ? |
|||
'image://https://elink-esua-epdc.oss-cn-qingdao.aliyuncs.com/epmet/test/internal/20240402/1e21790833b04e9abe61e2a8f40446cb.png' : |
|||
item.age < 18 ? |
|||
'image://https://elink-esua-epdc.oss-cn-qingdao.aliyuncs.com/epmet/test/internal/20240402/62c2b6a1ce834f8dae081b562b2579b9.png' : |
|||
item.age > 60 ? |
|||
'image://https://elink-esua-epdc.oss-cn-qingdao.aliyuncs.com/epmet/test/internal/20240402/bd586a4e7d0f4b3aa68c79067dfe1428.png' : |
|||
''; |
|||
} else if (item.gender === '2') { |
|||
// 如果 gender 等于 2,则根据 age 进行进一步的判断
|
|||
symbolUrl = item.age > 18 && item.age < 60 ? |
|||
'image://https://elink-esua-epdc.oss-cn-qingdao.aliyuncs.com/epmet/test/internal/20240402/3b44e3495dcd4d96a3c07baad1bd7c5e.png' : |
|||
item.age < 18 ? |
|||
'image://https://elink-esua-epdc.oss-cn-qingdao.aliyuncs.com/epmet/test/internal/20240402/0c38251bba644a6b8e4a925aea74c9a7.png' : |
|||
item.age > 60 ? |
|||
'image://https://elink-esua-epdc.oss-cn-qingdao.aliyuncs.com/epmet/test/internal/20240402/2465ff7633a94413bf4ed2aa778c2765.png' : |
|||
''; |
|||
} |
|||
} |
|||
return { |
|||
'name': item.name, |
|||
'id': index + 1, |
|||
'category': 0, |
|||
'symbolSize': item.id == this.data.houseId ? 50 : 30, |
|||
'symbol': symbolUrl, |
|||
label: { |
|||
"position": "bottom" |
|||
} |
|||
}; |
|||
}); |
|||
|
|||
let links = data.map((item, index) => ({ |
|||
"source": 0, |
|||
"target": index+1, |
|||
"name": item.houseHolderRel||'暂不清楚', |
|||
"label": { |
|||
"align": "center", |
|||
"fontSize": 12, |
|||
"color":"#9cbbf9" |
|||
}, |
|||
"lineStyle": { |
|||
"color": "#9cbbf9" |
|||
} |
|||
})) |
|||
links.splice(links.length-1,1) |
|||
let categories = data.map((item, index) => ({ |
|||
"name":item.houseHolderRel||'暂不清楚', |
|||
"id":index+1, |
|||
"itemStyle": { |
|||
"color": '#9cbbf9' |
|||
}, |
|||
})) |
|||
|
|||
console.log(nodeList,links,categories); |
|||
|
|||
var option = { |
|||
|
|||
legend: [{ |
|||
// selectedMode: 'single',
|
|||
// icon: 'circle'
|
|||
show: false |
|||
}], |
|||
series: [{ |
|||
type: 'graph', |
|||
layout: 'force', |
|||
// symbolSize: 10,
|
|||
force: { |
|||
repulsion: 80, |
|||
gravity: 0, |
|||
edgeLength: 100,//线条长度
|
|||
layoutAnimation: true, |
|||
}, |
|||
draggable: true, |
|||
roam: false, |
|||
nodeScaleRatio: 0, |
|||
focusNodeAdjacency: false, |
|||
lineStyle: { |
|||
normal: { |
|||
opacity: 0.5, |
|||
width: 1.5, |
|||
curveness: 0 |
|||
} |
|||
}, |
|||
categories: categories, |
|||
edgeSymbol: ['', 'arrow'], |
|||
// edgeSymbolSize: [80, 10],
|
|||
edgeLabel: { |
|||
normal: { |
|||
show: true, |
|||
textStyle: { |
|||
fontSize: 20 |
|||
}, |
|||
formatter(x) { |
|||
return x.data.name; |
|||
} |
|||
} |
|||
}, |
|||
label: { |
|||
show: true |
|||
}, |
|||
|
|||
data: nodeList, |
|||
links: links |
|||
}] |
|||
}; |
|||
chart.setOption(option); |
|||
}) |
|||
return chart; |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
@ -0,0 +1,6 @@ |
|||
{ |
|||
"component": true, |
|||
"usingComponents": { |
|||
"ec-canvas": "../../../../../../ec-canvas/ec-canvas" |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
<!--subpages/searchResult/pages/resiInfo/modules/graph/graph.wxml--> |
|||
<view class="hotlineComplaints"> |
|||
<ec-canvas id="hotlineComplaints" canvas-id="hotlineComplaints" ec="{{ ec }}"></ec-canvas> |
|||
</view> |
|||
@ -0,0 +1,20 @@ |
|||
/* subpages/searchResult/pages/resiInfo/modules/graph/graph.wxss */ |
|||
ec-canvas { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
.hotlineComplaints { |
|||
width: 100%; |
|||
height: 618rpx; |
|||
position: relative; |
|||
} |
|||
|
|||
.hotlineComplaints image{ |
|||
position: absolute; |
|||
width: 100%; |
|||
height: 100%; |
|||
z-index: 2; |
|||
top: 0; |
|||
left: 50%; |
|||
transform: translate(-50%); |
|||
} |
|||
@ -1,5 +1,9 @@ |
|||
{ |
|||
{ |
|||
"navigationBarTitleText": "房屋详情", |
|||
"usingComponents": { |
|||
"BusinessRecord": "../../../businessRecord/businessRecord" |
|||
"BusinessRecord": "../../../businessRecord/businessRecord", |
|||
"no-data": "../../../../components/noData/nodata", |
|||
"house-graph":"./component/graph/graph" |
|||
|
|||
} |
|||
} |
|||
@ -1,4 +1,4 @@ |
|||
<!--subpages/searchResult/pages/resiInfo/modules/graph/graph.wxml--> |
|||
<view class="hotlineComplaints"> |
|||
<ec-canvas id="hotlineComplaints" canvas-id="hotlineComplaints" ec="{{ ec }}"></ec-canvas> |
|||
</view> |
|||
</view> |
|||
Loading…
Reference in new issue