+   * @param {boolean} trimTrailing
2008
+   * @returns {string}
2009
+   */
2010
+  function parseConsecutiveLists(list, listType, trimTrailing) {
2011
+    // check if we caught 2 or more consecutive lists by mistake
2012
+    // we use the counterRgx, meaning if listType is UL we look for UL and vice versa
2013
+    let counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
2014
+    const subLists = [];
2015
+    let result = '';
2016
+
2017
+    if (list.search(counterRxg) !== -1) {
2018
+      (function parseCL(txt) {
2019
+        const pos = txt.search(counterRxg);
2020
+        if (pos !== -1) {
2021
+          // slice
2022
+          result += `\n\n<${listType}>${processListItems(txt.slice(0, pos), !!trimTrailing)}</${listType}>\n\n`;
2023
+
2024
+          // invert counterType and listType
2025
+          listType = (listType === 'ul') ? 'ol' : 'ul';
2026
+          counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm;
2027
+
2028
+          // recurse
2029
+          parseCL(txt.slice(pos));
2030
+        } else {
2031
+          result += `\n\n<${listType}>${processListItems(txt, !!trimTrailing)}</${listType}>\n\n`;
2032
+        }
2033
+      }(list));
2034
+      for (let i = 0; i < subLists.length; ++i) {
2035
+
2036
+      }
2037
+    } else {
2038
+      result = `\n\n<${listType}>${processListItems(list, !!trimTrailing)}</${listType}>\n\n`;
2039
+    }
2040
+
2041
+    return result;
2042
+  }
2043
+
2044
+  // attacklab: add sentinel to hack around khtml/safari bug:
2045
+  // http://bugs.webkit.org/show_bug.cgi?id=11231
2046
+  text += '~0';
2047
+
2048
+  // Re-usable pattern to match any entire ul or ol list:
2049
+  let wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
2050
+
2051
+  if (globals.gListLevel) {
2052
+    text = text.replace(wholeList, (wholeMatch, list, m2) => {
2053
+      const listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2054
+      return parseConsecutiveLists(list, listType, true);
2055
+    });
2056
+  } else {
2057
+    wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
2058
+    // wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
2059
+    text = text.replace(wholeList, (wholeMatch, m1, list, m3) => {
2060
+      const listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol';
2061
+      return parseConsecutiveLists(list, listType);
2062
+    });
2063
+  }
2064
+
2065
+  // attacklab: strip sentinel
2066
+  text = text.replace(/~0/, '');
2067
+
2068
+  text = globals.converter._dispatch('lists.after', text, options, globals);
2069
+  return text;
2070
+});
2071
+
2072
+/**
2073
+ * Remove one level of line-leading tabs or spaces
2074
+ */
2075
+showdown.subParser('outdent', (text) => {
2076
+  // attacklab: hack around Konqueror 3.5.4 bug:
2077
+  // "----------bug".replace(/^-/g,"") == "bug"
2078
+  text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width
2079
+
2080
+  // attacklab: clean up hack
2081
+  text = text.replace(/~0/g, '');
2082
+
2083
+  return text;
2084
+});
2085
+
2086
+/**
2087
+ *
2088
+ */
2089
+showdown.subParser('paragraphs', (text, options, globals) => {
2090
+  text = globals.converter._dispatch('paragraphs.before', text, options, globals);
2091
+  // Strip leading and trailing lines:
2092
+  text = text.replace(/^\n+/g, '');
2093
+  text = text.replace(/\n+$/g, '');
2094
+
2095
+  const grafs = text.split(/\n{2,}/g);
2096
+  const grafsOut = [];
2097
+  let end = grafs.length; // Wrap <p> tags
2098
+
2099
+  for (var i = 0; i < end; i++) {
2100
+    let str = grafs[i];
2101
+    // if this is an HTML marker, copy it
2102
+    if (str.search(/~(K|G)(\d+)\1/g) >= 0) {
2103
+      grafsOut.push(str);
2104
+    } else {
2105
+      str = showdown.subParser('spanGamut')(str, options, globals);
2106
+      str = str.replace(/^([ \t]*)/g, '<p>');
2107
+      str += '</p>';
2108
+      grafsOut.push(str);
2109
+    }
2110
+  }
2111
+
2112
+  /** Unhashify HTML blocks */
2113
+  end = grafsOut.length;
2114
+  for (i = 0; i < end; i++) {
2115
+    let blockText = '';
2116
+    let grafsOutIt = grafsOut[i];
2117
+    let codeFlag = false;
2118
+    // if this is a marker for an html block...
2119
+    while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) {
2120
+      const delim = RegExp.$1;
2121
+      const num = RegExp.$2;
2122
+
2123
+      if (delim === 'K') {
2124
+        blockText = globals.gHtmlBlocks[num];
2125
+      } else {
2126
+        // we need to check if ghBlock is a false positive
2127
+        if (codeFlag) {
2128
+          // use encoded version of all text
2129
+          blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text);
2130
+        } else {
2131
+          blockText = globals.ghCodeBlocks[num].codeblock;
2132
+        }
2133
+      }
2134
+      blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs
2135
+
2136
+      grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText);
2137
+      // Check if grafsOutIt is a pre->code
2138
+      if (/^<pre\b[^>]*>\s*<code\b[^>]*>/.test(grafsOutIt)) {
2139
+        codeFlag = true;
2140
+      }
2141
+    }
2142
+    grafsOut[i] = grafsOutIt;
2143
+  }
2144
+  text = grafsOut.join('\n\n');
2145
+  // Strip leading and trailing lines:
2146
+  text = text.replace(/^\n+/g, '');
2147
+  text = text.replace(/\n+$/g, '');
2148
+  return globals.converter._dispatch('paragraphs.after', text, options, globals);
2149
+});
2150
+
2151
+/**
2152
+ * Run extension
2153
+ */
2154
+showdown.subParser('runExtension', (ext, text, options, globals) => {
2155
+  if (ext.filter) {
2156
+    text = ext.filter(text, globals.converter, options);
2157
+  } else if (ext.regex) {
2158
+    // TODO remove this when old extension loading mechanism is deprecated
2159
+    let re = ext.regex;
2160
+    if (!re instanceof RegExp) {
2161
+      re = new RegExp(re, 'g');
2162
+    }
2163
+    text = text.replace(re, ext.replace);
2164
+  }
2165
+
2166
+  return text;
2167
+});
2168
+
2169
+/**
2170
+ * These are all the transformations that occur *within* block-level
2171
+ * tags like paragraphs, headers, and list items.
2172
+ */
2173
+showdown.subParser('spanGamut', (text, options, globals) => {
2174
+  text = globals.converter._dispatch('spanGamut.before', text, options, globals);
2175
+  text = showdown.subParser('codeSpans')(text, options, globals);
2176
+  text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals);
2177
+  text = showdown.subParser('encodeBackslashEscapes')(text, options, globals);
2178
+
2179
+  // Process anchor and image tags. Images must come first,
2180
+  // because ![foo][f] looks like an anchor.
2181
+  text = showdown.subParser('images')(text, options, globals);
2182
+  text = showdown.subParser('anchors')(text, options, globals);
2183
+
2184
+  // Make links out of things like `<http://example.com/>`
2185
+  // Must come after _DoAnchors(), because you can use < and >
2186
+  // delimiters in inline links like [this](<url>).
2187
+  text = showdown.subParser('autoLinks')(text, options, globals);
2188
+  text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals);
2189
+  text = showdown.subParser('italicsAndBold')(text, options, globals);
2190
+  text = showdown.subParser('strikethrough')(text, options, globals);
2191
+
2192
+  // Do hard breaks:
2193
+  text = text.replace(/  +\n/g, ' <br />\n');
2194
+
2195
+  text = globals.converter._dispatch('spanGamut.after', text, options, globals);
2196
+  return text;
2197
+});
2198
+
2199
+showdown.subParser('strikethrough', (text, options, globals) => {
2200
+  if (options.strikethrough) {
2201
+    text = globals.converter._dispatch('strikethrough.before', text, options, globals);
2202
+    text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '<del>$1</del>');
2203
+    text = globals.converter._dispatch('strikethrough.after', text, options, globals);
2204
+  }
2205
+
2206
+  return text;
2207
+});
2208
+
2209
+/**
2210
+ * Strip any lines consisting only of spaces and tabs.
2211
+ * This makes subsequent regexs easier to write, because we can
2212
+ * match consecutive blank lines with /\n+/ instead of something
2213
+ * contorted like /[ \t]*\n+/
2214
+ */
2215
+showdown.subParser('stripBlankLines', (text) => text.replace(/^[ \t]+$/mg, ''));
2216
+
2217
+/**
2218
+ * Strips link definitions from text, stores the URLs and titles in
2219
+ * hash references.
2220
+ * Link defs are in the form: ^[id]: url "optional title"
2221
+ *
2222
+ * ^[ ]{0,3}\[(.+)\]: // id = $1  attacklab: g_tab_width - 1
2223
+ * [ \t]*
2224
+ * \n?                  // maybe *one* newline
2225
+ * [ \t]*
2226
+ * <?(\S+?)>?          // url = $2
2227
+ * [ \t]*
2228
+ * \n?                // maybe one newline
2229
+ * [ \t]*
2230
+ * (?:
2231
+ * (\n*)              // any lines skipped = $3 attacklab: lookbehind removed
2232
+ * ["(]
2233
+ * (.+?)              // title = $4
2234
+ * [")]
2235
+ * [ \t]*
2236
+ * )?                 // title is optional
2237
+ * (?:\n+|$)
2238
+ * /gm,
2239
+ * function(){...});
2240
+ *
2241
+ */
2242
+showdown.subParser('stripLinkDefinitions', (text, options, globals) => {
2243
+  const regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*<?(\S+?)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm;
2244
+
2245
+  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
2246
+  text += '~0';
2247
+
2248
+  text = text.replace(regex, (wholeMatch, linkId, url, width, height, blankLines, title) => {
2249
+    linkId = linkId.toLowerCase();
2250
+    globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive
2251
+
2252
+    if (blankLines) {
2253
+      // Oops, found blank lines, so it's not a title.
2254
+      // Put back the parenthetical statement we stole.
2255
+      return blankLines + title;
2256
+    }
2257
+    if (title) {
2258
+      globals.gTitles[linkId] = title.replace(/"|'/g, '&quot;');
2259
+    }
2260
+    if (options.parseImgDimensions && width && height) {
2261
+      globals.gDimensions[linkId] = {
2262
+        width,
2263
+        height,
2264
+      };
2265
+    }
2266
+
2267
+    // Completely remove the definition from the text
2268
+    return '';
2269
+  });
2270
+
2271
+  // attacklab: strip sentinel
2272
+  text = text.replace(/~0/, '');
2273
+
2274
+  return text;
2275
+});
2276
+
2277
+showdown.subParser('tables', (text, options, globals) => {
2278
+  if (!options.tables) {
2279
+    return text;
2280
+  }
2281
+
2282
+  const tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm;
2283
+
2284
+  function parseStyles(sLine) {
2285
+    if (/^:[ \t]*--*$/.test(sLine)) {
2286
+      return ' style="text-align:left;"';
2287
+    } if (/^--*[ \t]*:[ \t]*$/.test(sLine)) {
2288
+      return ' style="text-align:right;"';
2289
+    } if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) {
2290
+      return ' style="text-align:center;"';
2291
+    }
2292
+    return '';
2293
+  }
2294
+
2295
+  function parseHeaders(header, style) {
2296
+    let id = '';
2297
+    header = header.trim();
2298
+    if (options.tableHeaderId) {
2299
+      id = ` id="${header.replace(/ /g, '_').toLowerCase()}"`;
2300
+    }
2301
+    header = showdown.subParser('spanGamut')(header, options, globals);
2302
+
2303
+    return `<th${id}${style}>${header}</th>\n`;
2304
+  }
2305
+
2306
+  function parseCells(cell, style) {
2307
+    const subText = showdown.subParser('spanGamut')(cell, options, globals);
2308
+    return `<td${style}>${subText}</td>\n`;
2309
+  }
2310
+
2311
+  function buildTable(headers, cells) {
2312
+    let tb = '<table>\n<thead>\n<tr>\n';
2313
+    const tblLgn = headers.length;
2314
+
2315
+    for (var i = 0; i < tblLgn; ++i) {
2316
+      tb += headers[i];
2317
+    }
2318
+    tb += '</tr>\n</thead>\n<tbody>\n';
2319
+
2320
+    for (i = 0; i < cells.length; ++i) {
2321
+      tb += '<tr>\n';
2322
+      for (let ii = 0; ii < tblLgn; ++ii) {
2323
+        tb += cells[i][ii];
2324
+      }
2325
+      tb += '</tr>\n';
2326
+    }
2327
+    tb += '</tbody>\n</table>\n';
2328
+    return tb;
2329
+  }
2330
+
2331
+  text = globals.converter._dispatch('tables.before', text, options, globals);
2332
+
2333
+  text = text.replace(tableRgx, (rawTable) => {
2334
+    let i; const
2335
+      tableLines = rawTable.split('\n');
2336
+
2337
+    // strip wrong first and last column if wrapped tables are used
2338
+    for (i = 0; i < tableLines.length; ++i) {
2339
+      if (/^[ \t]{0,3}\|/.test(tableLines[i])) {
2340
+        tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, '');
2341
+      }
2342
+      if (/\|[ \t]*$/.test(tableLines[i])) {
2343
+        tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, '');
2344
+      }
2345
+    }
2346
+
2347
+    const rawHeaders = tableLines[0].split('|').map((s) => s.trim());
2348
+    const rawStyles = tableLines[1].split('|').map((s) => s.trim());
2349
+    const rawCells = [];
2350
+    const headers = [];
2351
+    const styles = [];
2352
+    const cells = [];
2353
+
2354
+    tableLines.shift();
2355
+    tableLines.shift();
2356
+
2357
+    for (i = 0; i < tableLines.length; ++i) {
2358
+      if (tableLines[i].trim() === '') {
2359
+        continue;
2360
+      }
2361
+      rawCells.push(
2362
+        tableLines[i]
2363
+          .split('|')
2364
+          .map((s) => s.trim()),
2365
+      );
2366
+    }
2367
+
2368
+    if (rawHeaders.length < rawStyles.length) {
2369
+      return rawTable;
2370
+    }
2371
+
2372
+    for (i = 0; i < rawStyles.length; ++i) {
2373
+      styles.push(parseStyles(rawStyles[i]));
2374
+    }
2375
+
2376
+    for (i = 0; i < rawHeaders.length; ++i) {
2377
+      if (showdown.helper.isUndefined(styles[i])) {
2378
+        styles[i] = '';
2379
+      }
2380
+      headers.push(parseHeaders(rawHeaders[i], styles[i]));
2381
+    }
2382
+
2383
+    for (i = 0; i < rawCells.length; ++i) {
2384
+      const row = [];
2385
+      for (let ii = 0; ii < headers.length; ++ii) {
2386
+        if (showdown.helper.isUndefined(rawCells[i][ii])) {
2387
+
2388
+        }
2389
+        row.push(parseCells(rawCells[i][ii], styles[ii]));
2390
+      }
2391
+      cells.push(row);
2392
+    }
2393
+
2394
+    return buildTable(headers, cells);
2395
+  });
2396
+
2397
+  text = globals.converter._dispatch('tables.after', text, options, globals);
2398
+
2399
+  return text;
2400
+});
2401
+
2402
+/**
2403
+ * Swap back in all the special characters we've hidden.
2404
+ */
2405
+showdown.subParser('unescapeSpecialChars', (text) => {
2406
+  text = text.replace(/~E(\d+)E/g, (wholeMatch, m1) => {
2407
+    const charCodeToReplace = parseInt(m1);
2408
+    return String.fromCharCode(charCodeToReplace);
2409
+  });
2410
+  return text;
2411
+});
2412
+module.exports = showdown;

+ 206 - 0
src/utils/wxParse/wxDiscode.js

@@ -0,0 +1,206 @@
1
+// HTML 支持的数学符号
2
+function strNumDiscode(str) {
3
+  str = str.replace(/&forall;/g, '∀');
4
+  str = str.replace(/&part;/g, '∂');
5
+  str = str.replace(/&exists;/g, '∃');
6
+  str = str.replace(/&empty;/g, '∅');
7
+  str = str.replace(/&nabla;/g, '∇');
8
+  str = str.replace(/&isin;/g, '∈');
9
+  str = str.replace(/&notin;/g, '∉');
10
+  str = str.replace(/&ni;/g, '∋');
11
+  str = str.replace(/&prod;/g, '∏');
12
+  str = str.replace(/&sum;/g, '∑');
13
+  str = str.replace(/&minus;/g, '−');
14
+  str = str.replace(/&lowast;/g, '∗');
15
+  str = str.replace(/&radic;/g, '√');
16
+  str = str.replace(/&prop;/g, '∝');
17
+  str = str.replace(/&infin;/g, '∞');
18
+  str = str.replace(/&ang;/g, '∠');
19
+  str = str.replace(/&and;/g, '∧');
20
+  str = str.replace(/&or;/g, '∨');
21
+  str = str.replace(/&cap;/g, '∩');
22
+  str = str.replace(/&cap;/g, '∪');
23
+  str = str.replace(/&int;/g, '∫');
24
+  str = str.replace(/&there4;/g, '∴');
25
+  str = str.replace(/&sim;/g, '∼');
26
+  str = str.replace(/&cong;/g, '≅');
27
+  str = str.replace(/&asymp;/g, '≈');
28
+  str = str.replace(/&ne;/g, '≠');
29
+  str = str.replace(/&le;/g, '≤');
30
+  str = str.replace(/&ge;/g, '≥');
31
+  str = str.replace(/&sub;/g, '⊂');
32
+  str = str.replace(/&sup;/g, '⊃');
33
+  str = str.replace(/&nsub;/g, '⊄');
34
+  str = str.replace(/&sube;/g, '⊆');
35
+  str = str.replace(/&supe;/g, '⊇');
36
+  str = str.replace(/&oplus;/g, '⊕');
37
+  str = str.replace(/&otimes;/g, '⊗');
38
+  str = str.replace(/&perp;/g, '⊥');
39
+  str = str.replace(/&sdot;/g, '⋅');
40
+  return str;
41
+}
42
+
43
+// HTML 支持的希腊字母
44
+function strGreeceDiscode(str) {
45
+  str = str.replace(/&Alpha;/g, 'Α');
46
+  str = str.replace(/&Beta;/g, 'Β');
47
+  str = str.replace(/&Gamma;/g, 'Γ');
48
+  str = str.replace(/&Delta;/g, 'Δ');
49
+  str = str.replace(/&Epsilon;/g, 'Ε');
50
+  str = str.replace(/&Zeta;/g, 'Ζ');
51
+  str = str.replace(/&Eta;/g, 'Η');
52
+  str = str.replace(/&Theta;/g, 'Θ');
53
+  str = str.replace(/&Iota;/g, 'Ι');
54
+  str = str.replace(/&Kappa;/g, 'Κ');
55
+  str = str.replace(/&Lambda;/g, 'Λ');
56
+  str = str.replace(/&Mu;/g, 'Μ');
57
+  str = str.replace(/&Nu;/g, 'Ν');
58
+  str = str.replace(/&Xi;/g, 'Ν');
59
+  str = str.replace(/&Omicron;/g, 'Ο');
60
+  str = str.replace(/&Pi;/g, 'Π');
61
+  str = str.replace(/&Rho;/g, 'Ρ');
62
+  str = str.replace(/&Sigma;/g, 'Σ');
63
+  str = str.replace(/&Tau;/g, 'Τ');
64
+  str = str.replace(/&Upsilon;/g, 'Υ');
65
+  str = str.replace(/&Phi;/g, 'Φ');
66
+  str = str.replace(/&Chi;/g, 'Χ');
67
+  str = str.replace(/&Psi;/g, 'Ψ');
68
+  str = str.replace(/&Omega;/g, 'Ω');
69
+
70
+  str = str.replace(/&alpha;/g, 'α');
71
+  str = str.replace(/&beta;/g, 'β');
72
+  str = str.replace(/&gamma;/g, 'γ');
73
+  str = str.replace(/&delta;/g, 'δ');
74
+  str = str.replace(/&epsilon;/g, 'ε');
75
+  str = str.replace(/&zeta;/g, 'ζ');
76
+  str = str.replace(/&eta;/g, 'η');
77
+  str = str.replace(/&theta;/g, 'θ');
78
+  str = str.replace(/&iota;/g, 'ι');
79
+  str = str.replace(/&kappa;/g, 'κ');
80
+  str = str.replace(/&lambda;/g, 'λ');
81
+  str = str.replace(/&mu;/g, 'μ');
82
+  str = str.replace(/&nu;/g, 'ν');
83
+  str = str.replace(/&xi;/g, 'ξ');
84
+  str = str.replace(/&omicron;/g, 'ο');
85
+  str = str.replace(/&pi;/g, 'π');
86
+  str = str.replace(/&rho;/g, 'ρ');
87
+  str = str.replace(/&sigmaf;/g, 'ς');
88
+  str = str.replace(/&sigma;/g, 'σ');
89
+  str = str.replace(/&tau;/g, 'τ');
90
+  str = str.replace(/&upsilon;/g, 'υ');
91
+  str = str.replace(/&phi;/g, 'φ');
92
+  str = str.replace(/&chi;/g, 'χ');
93
+  str = str.replace(/&psi;/g, 'ψ');
94
+  str = str.replace(/&omega;/g, 'ω');
95
+  str = str.replace(/&thetasym;/g, 'ϑ');
96
+  str = str.replace(/&upsih;/g, 'ϒ');
97
+  str = str.replace(/&piv;/g, 'ϖ');
98
+  str = str.replace(/&middot;/g, '·');
99
+  return str;
100
+}
101
+
102
+//
103
+
104
+function strcharacterDiscode(str) {
105
+  // 加入常用解析
106
+  str = str.replace(/&nbsp;/g, ' ');
107
+  str = str.replace(/&quot;/g, "'");
108
+  str = str.replace(/&amp;/g, '&');
109
+  // str = str.replace(/&lt;/g, '‹');
110
+  // str = str.replace(/&gt;/g, '›');
111
+
112
+  str = str.replace(/&lt;/g, '<');
113
+  str = str.replace(/&gt;/g, '>');
114
+  str = str.replace(/&#8226;/g, '•');
115
+
116
+  return str;
117
+}
118
+
119
+// HTML 支持的其他实体
120
+function strOtherDiscode(str) {
121
+  str = str.replace(/&OElig;/g, 'Œ');
122
+  str = str.replace(/&oelig;/g, 'œ');
123
+  str = str.replace(/&Scaron;/g, 'Š');
124
+  str = str.replace(/&scaron;/g, 'š');
125
+  str = str.replace(/&Yuml;/g, 'Ÿ');
126
+  str = str.replace(/&fnof;/g, 'ƒ');
127
+  str = str.replace(/&circ;/g, 'ˆ');
128
+  str = str.replace(/&tilde;/g, '˜');
129
+  str = str.replace(/&ensp;/g, '');
130
+  str = str.replace(/&emsp;/g, '');
131
+  str = str.replace(/&thinsp;/g, '');
132
+  str = str.replace(/&zwnj;/g, '');
133
+  str = str.replace(/&zwj;/g, '');
134
+  str = str.replace(/&lrm;/g, '');
135
+  str = str.replace(/&rlm;/g, '');
136
+  str = str.replace(/&ndash;/g, '–');
137
+  str = str.replace(/&mdash;/g, '—');
138
+  str = str.replace(/&lsquo;/g, '‘');
139
+  str = str.replace(/&rsquo;/g, '’');
140
+  str = str.replace(/&sbquo;/g, '‚');
141
+  str = str.replace(/&ldquo;/g, '“');
142
+  str = str.replace(/&rdquo;/g, '”');
143
+  str = str.replace(/&bdquo;/g, '„');
144
+  str = str.replace(/&dagger;/g, '†');
145
+  str = str.replace(/&Dagger;/g, '‡');
146
+  str = str.replace(/&bull;/g, '•');
147
+  str = str.replace(/&hellip;/g, '…');
148
+  str = str.replace(/&permil;/g, '‰');
149
+  str = str.replace(/&prime;/g, '′');
150
+  str = str.replace(/&Prime;/g, '″');
151
+  str = str.replace(/&lsaquo;/g, '‹');
152
+  str = str.replace(/&rsaquo;/g, '›');
153
+  str = str.replace(/&oline;/g, '‾');
154
+  str = str.replace(/&euro;/g, '€');
155
+  str = str.replace(/&trade;/g, '™');
156
+
157
+  str = str.replace(/&larr;/g, '←');
158
+  str = str.replace(/&uarr;/g, '↑');
159
+  str = str.replace(/&rarr;/g, '→');
160
+  str = str.replace(/&darr;/g, '↓');
161
+  str = str.replace(/&harr;/g, '↔');
162
+  str = str.replace(/&crarr;/g, '↵');
163
+  str = str.replace(/&lceil;/g, '⌈');
164
+  str = str.replace(/&rceil;/g, '⌉');
165
+
166
+  str = str.replace(/&lfloor;/g, '⌊');
167
+  str = str.replace(/&rfloor;/g, '⌋');
168
+  str = str.replace(/&loz;/g, '◊');
169
+  str = str.replace(/&spades;/g, '♠');
170
+  str = str.replace(/&clubs;/g, '♣');
171
+  str = str.replace(/&hearts;/g, '♥');
172
+
173
+  str = str.replace(/&diams;/g, '♦');
174
+  str = str.replace(/&#39;/g, '\'');
175
+  return str;
176
+}
177
+
178
+function strMoreDiscode(str) {
179
+  str = str.replace(/\r\n/g, '');
180
+  str = str.replace(/\n/g, '');
181
+
182
+  str = str.replace(/code/g, 'wxxxcode-style');
183
+  return str;
184
+}
185
+
186
+function strDiscode(str) {
187
+  str = strNumDiscode(str);
188
+  str = strGreeceDiscode(str);
189
+  str = strcharacterDiscode(str);
190
+  str = strOtherDiscode(str);
191
+  str = strMoreDiscode(str);
192
+  return str;
193
+}
194
+function urlToHttpUrl(url, rep) {
195
+  const patt1 = new RegExp('^//');
196
+  const result = patt1.test(url);
197
+  if (result) {
198
+    url = `${rep}:${url}`;
199
+  }
200
+  return url;
201
+}
202
+
203
+module.exports = {
204
+  strDiscode,
205
+  urlToHttpUrl,
206
+};

+ 159 - 0
src/utils/wxParse/wxParse.js

@@ -0,0 +1,159 @@
1
+/**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ *
6
+ * github地址: https://github.com/icindy/wxParse
7
+ *
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */
11
+
12
+/**
13
+ * utils函数引入
14
+ * */
15
+import showdown from './showdown.js';
16
+import HtmlToJson from './html2json.js';
17
+/**
18
+ * 配置及公有属性
19
+ * */
20
+let realWindowWidth = 0;
21
+let realWindowHeight = 0;
22
+wx.getSystemInfo({
23
+  success(res) {
24
+    realWindowWidth = res.windowWidth;
25
+    realWindowHeight = res.windowHeight;
26
+  },
27
+});
28
+/**
29
+ * 主函数入口区
30
+ * */
31
+function wxParse(bindName = 'wxParseData', type = 'html', data = '<div class="color:red;">数据不能为空</div>', target, imagePadding) {
32
+  const that = target;
33
+  let transData = {};// 存放转化后的数据
34
+  if (type == 'html') {
35
+    transData = HtmlToJson.html2json(data, bindName);
36
+    console.log(JSON.stringify(transData, ' ', ' '));
37
+  } else if (type == 'md' || type == 'markdown') {
38
+    const converter = new showdown.Converter();
39
+    const html = converter.makeHtml(data);
40
+    transData = HtmlToJson.html2json(html, bindName);
41
+    console.log(JSON.stringify(transData, ' ', ' '));
42
+  }
43
+  transData.view = {};
44
+  transData.view.imagePadding = 0;
45
+  if (typeof (imagePadding) !== 'undefined') {
46
+    transData.view.imagePadding = imagePadding;
47
+  }
48
+  const bindData = {};
49
+  bindData[bindName] = transData;
50
+  that.setData(bindData);
51
+  that.wxParseImgLoad = wxParseImgLoad;
52
+  that.wxParseImgTap = wxParseImgTap;
53
+}
54
+// 图片点击事件
55
+function wxParseImgTap(e) {
56
+  const that = this;
57
+  const nowImgUrl = e.target.dataset.src;
58
+  const tagFrom = e.target.dataset.from;
59
+  if (typeof (tagFrom) !== 'undefined' && tagFrom.length > 0) {
60
+    wx.previewImage({
61
+      current: nowImgUrl, // 当前显示图片的http链接
62
+      urls: that.data[tagFrom].imageUrls, // 需要预览的图片http链接列表
63
+    });
64
+  }
65
+}
66
+
67
+/**
68
+ * 图片视觉宽高计算函数区
69
+ * */
70
+function wxParseImgLoad(e) {
71
+  const that = this;
72
+  const tagFrom = e.target.dataset.from;
73
+  const { idx } = e.target.dataset;
74
+  if (typeof (tagFrom) !== 'undefined' && tagFrom.length > 0) {
75
+    calMoreImageInfo(e, idx, that, tagFrom);
76
+  }
77
+}
78
+// 假循环获取计算图片视觉最佳宽高
79
+function calMoreImageInfo(e, idx, that, bindName) {
80
+  const temData = that.data[bindName];
81
+  if (!temData || temData.images.length == 0) {
82
+    return;
83
+  }
84
+  const temImages = temData.images;
85
+  // 因为无法获取view宽度 需要自定义padding进行计算,稍后处理
86
+  const recal = wxAutoImageCal(e.detail.width, e.detail.height, that, bindName);
87
+  // temImages[idx].width = recal.imageWidth;
88
+  // temImages[idx].height = recal.imageheight;
89
+  // temData.images = temImages;
90
+  // var bindData = {};
91
+  // bindData[bindName] = temData;
92
+  // that.setData(bindData);
93
+  const { index } = temImages[idx];
94
+  let key = `${bindName}`;
95
+  for (const i of index.split('.')) key += `.nodes[${i}]`;
96
+  const keyW = `${key}.width`;
97
+  const keyH = `${key}.height`;
98
+  that.setData({
99
+    [keyW]: recal.imageWidth,
100
+    [keyH]: recal.imageheight,
101
+  });
102
+}
103
+
104
+// 计算视觉优先的图片宽高
105
+function wxAutoImageCal(originalWidth, originalHeight, that, bindName) {
106
+  // 获取图片的原始长宽
107
+  let windowWidth = 0; let
108
+    windowHeight = 0;
109
+  let autoWidth = 0; let
110
+    autoHeight = 0;
111
+  const results = {};
112
+  const padding = that.data[bindName].view.imagePadding;
113
+  windowWidth = realWindowWidth - 2 * padding;
114
+  windowHeight = realWindowHeight;
115
+  // 判断按照那种方式进行缩放
116
+  // console.log("windowWidth" + windowWidth);
117
+  if (originalWidth > windowWidth) { // 在图片width大于手机屏幕width时候
118
+    autoWidth = windowWidth;
119
+    // console.log("autoWidth" + autoWidth);
120
+    autoHeight = (autoWidth * originalHeight) / originalWidth;
121
+    // console.log("autoHeight" + autoHeight);
122
+    results.imageWidth = autoWidth;
123
+    results.imageheight = autoHeight;
124
+  } else { // 否则展示原来的数据
125
+    results.imageWidth = originalWidth;
126
+    results.imageheight = originalHeight;
127
+  }
128
+  return results;
129
+}
130
+
131
+function wxParseTemArray(temArrayName, bindNameReg, total, that) {
132
+  const array = [];
133
+  const temData = that.data;
134
+  let obj = null;
135
+  for (let i = 0; i < total; i++) {
136
+    const simArr = temData[bindNameReg + i].nodes;
137
+    array.push(simArr);
138
+  }
139
+
140
+  temArrayName = temArrayName || 'wxParseTemArray';
141
+  obj = JSON.parse(`{"${temArrayName}":""}`);
142
+  obj[temArrayName] = array;
143
+  that.setData(obj);
144
+}
145
+
146
+/**
147
+ * 配置emojis
148
+ *
149
+ */
150
+
151
+function emojisInit(reg = '', baseSrc = '/wxParse/emojis/', emojis) {
152
+  HtmlToJson.emojisInit(reg, baseSrc, emojis);
153
+}
154
+
155
+module.exports = {
156
+  wxParse,
157
+  wxParseTemArray,
158
+  emojisInit,
159
+};

+ 967 - 0
src/utils/wxParse/wxParse.wxml

@@ -0,0 +1,967 @@
1
+<!--**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ * 
6
+ * github地址: https://github.com/icindy/wxParse
7
+ * 
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */-->
11
+
12
+<!--基础元素-->
13
+<template name="wxParseVideo">
14
+  <!--增加video标签支持,并循环添加-->
15
+  <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
16
+    <video class="{{item.classStr}} wxParse-{{item.tag}}-video" src="{{item.attr.src}}"></video>
17
+  </view>
18
+</template>
19
+
20
+<template name="wxParseImg">
21
+  <image class="{{item.classStr}} wxParse-{{item.tag}}" data-from="{{item.from}}" data-src="{{item.attr.src}}" data-idx="{{item.imgIndex}}" src="{{item.attr.src}}" bindload="wxParseImgLoad" bindtap="wxParseImgTap" mode="widthFix" style="width:100%;"
22
+  />
23
+</template>
24
+
25
+<template name="WxEmojiView">
26
+  <view class="WxEmojiView wxParse-inline" style="{{item.styleStr}}">
27
+    <block wx:for="{{item.textArray}}" wx:key="">
28
+      <block class="{{item.text == '\\n' ? 'wxParse-hide':''}}" wx:if="{{item.node == 'text'}}">{{item.text}}</block>
29
+      <block wx:elif="{{item.node == 'element'}}">
30
+        <image class="wxEmoji" src="{{item.baseSrc}}{{item.text}}" />
31
+      </block>
32
+    </block>
33
+  </view>
34
+</template>
35
+
36
+<template name="WxParseBr">
37
+  <text>\n</text>
38
+</template>
39
+<!--入口模版-->
40
+
41
+<template name="wxParse">
42
+  <block wx:for="{{wxParseData}}" wx:key="">
43
+    <template is="wxParse0" data="{{item}}" />
44
+  </block>
45
+</template>
46
+
47
+
48
+<!--循环模版-->
49
+<template name="wxParse0">
50
+  <!--<template is="wxParse1" data="{{item}}" />-->
51
+  <!--判断是否是标签节点-->
52
+  <block wx:if="{{item.node == 'element'}}">
53
+    <block wx:if="{{item.tag == 'button'}}">
54
+      <button type="default" size="mini">
55
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
56
+          <template is="wxParse1" data="{{item}}" />
57
+        </block>
58
+      </button>
59
+    </block>
60
+    <!--li类型-->
61
+    <block wx:elif="{{item.tag == 'li'}}">
62
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
63
+        <view class="{{item.classStr}} wxParse-li-inner">
64
+          <view class="{{item.classStr}} wxParse-li-text">
65
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
66
+          </view>
67
+          <view class="{{item.classStr}} wxParse-li-text">
68
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
69
+              <template is="wxParse1" data="{{item}}" />
70
+            </block>
71
+          </view>
72
+        </view>
73
+      </view>
74
+    </block>
75
+
76
+    <!--video类型-->
77
+    <block wx:elif="{{item.tag == 'video'}}">
78
+      <template is="wxParseVideo" data="{{item}}" />
79
+    </block>
80
+
81
+    <!--img类型-->
82
+    <block wx:elif="{{item.tag == 'img'}}">
83
+      <template is="wxParseImg" data="{{item}}" />
84
+    </block>
85
+
86
+    <!--a类型-->
87
+    <block wx:elif="{{item.tag == 'a'}}">
88
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
89
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
90
+          <template is="wxParse1" data="{{item}}" />
91
+        </block>
92
+      </view>
93
+    </block>
94
+    <block wx:elif="{{item.tag == 'table'}}">
95
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
96
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
97
+          <template is="wxParse1" data="{{item}}" />
98
+        </block>
99
+      </view>
100
+    </block>
101
+
102
+    <block wx:elif="{{item.tag == 'br'}}">
103
+      <template is="WxParseBr"></template>
104
+    </block>
105
+    <!--其他块级标签-->
106
+    <block wx:elif="{{item.tagType == 'block'}}">
107
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
108
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
109
+          <template is="wxParse1" data="{{item}}" />
110
+        </block>
111
+      </view>
112
+    </block>
113
+
114
+    <!--内联标签-->
115
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
116
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
117
+        <template is="wxParse1" data="{{item}}" />
118
+      </block>
119
+    </view>
120
+
121
+  </block>
122
+
123
+  <!--判断是否是文本节点-->
124
+  <block wx:elif="{{item.node == 'text'}}">
125
+    <!--如果是,直接进行-->
126
+    <template is="WxEmojiView" data="{{item}}" />
127
+  </block>
128
+
129
+</template>
130
+
131
+
132
+
133
+<!--循环模版-->
134
+<template name="wxParse1">
135
+  <!--<template is="wxParse2" data="{{item}}" />-->
136
+  <!--判断是否是标签节点-->
137
+  <block wx:if="{{item.node == 'element'}}">
138
+    <block wx:if="{{item.tag == 'button'}}">
139
+      <button type="default" size="mini">
140
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
141
+          <template is="wxParse2" data="{{item}}" />
142
+        </block>
143
+      </button>
144
+    </block>
145
+    <!--li类型-->
146
+    <block wx:elif="{{item.tag == 'li'}}">
147
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
148
+        <view class="{{item.classStr}} wxParse-li-inner">
149
+          <view class="{{item.classStr}} wxParse-li-text">
150
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
151
+          </view>
152
+          <view class="{{item.classStr}} wxParse-li-text">
153
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
154
+              <template is="wxParse2" data="{{item}}" />
155
+            </block>
156
+          </view>
157
+        </view>
158
+      </view>
159
+    </block>
160
+
161
+    <!--video类型-->
162
+    <block wx:elif="{{item.tag == 'video'}}">
163
+      <template is="wxParseVideo" data="{{item}}" />
164
+    </block>
165
+
166
+    <!--img类型-->
167
+    <block wx:elif="{{item.tag == 'img'}}">
168
+      <template is="wxParseImg" data="{{item}}" />
169
+    </block>
170
+
171
+    <!--a类型-->
172
+    <block wx:elif="{{item.tag == 'a'}}">
173
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
174
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
175
+          <template is="wxParse2" data="{{item}}" />
176
+        </block>
177
+      </view>
178
+    </block>
179
+
180
+    <block wx:elif="{{item.tag == 'br'}}">
181
+      <template is="WxParseBr"></template>
182
+    </block>
183
+    <!--其他块级标签-->
184
+    <block wx:elif="{{item.tagType == 'block'}}">
185
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
186
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
187
+          <template is="wxParse2" data="{{item}}" />
188
+        </block>
189
+      </view>
190
+    </block>
191
+
192
+    <!--内联标签-->
193
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
194
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
195
+        <template is="wxParse2" data="{{item}}" />
196
+      </block>
197
+    </view>
198
+
199
+  </block>
200
+
201
+  <!--判断是否是文本节点-->
202
+  <block wx:elif="{{item.node == 'text'}}">
203
+    <!--如果是,直接进行-->
204
+    <template is="WxEmojiView" data="{{item}}" />
205
+  </block>
206
+
207
+</template>
208
+
209
+
210
+<!--循环模版-->
211
+<template name="wxParse2">
212
+  <!--<template is="wxParse3" data="{{item}}" />-->
213
+  <!--判断是否是标签节点-->
214
+  <block wx:if="{{item.node == 'element'}}">
215
+    <block wx:if="{{item.tag == 'button'}}">
216
+      <button type="default" size="mini">
217
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
218
+          <template is="wxParse3" data="{{item}}" />
219
+        </block>
220
+      </button>
221
+    </block>
222
+    <!--li类型-->
223
+    <block wx:elif="{{item.tag == 'li'}}">
224
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
225
+        <view class="{{item.classStr}} wxParse-li-inner">
226
+          <view class="{{item.classStr}} wxParse-li-text">
227
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
228
+          </view>
229
+          <view class="{{item.classStr}} wxParse-li-text">
230
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
231
+              <template is="wxParse3" data="{{item}}" />
232
+            </block>
233
+          </view>
234
+        </view>
235
+      </view>
236
+    </block>
237
+
238
+    <!--video类型-->
239
+    <block wx:elif="{{item.tag == 'video'}}">
240
+      <template is="wxParseVideo" data="{{item}}" />
241
+    </block>
242
+
243
+    <!--img类型-->
244
+    <block wx:elif="{{item.tag == 'img'}}">
245
+      <template is="wxParseImg" data="{{item}}" />
246
+    </block>
247
+
248
+    <!--a类型-->
249
+    <block wx:elif="{{item.tag == 'a'}}">
250
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
251
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
252
+          <template is="wxParse3" data="{{item}}" />
253
+        </block>
254
+      </view>
255
+    </block>
256
+
257
+    <block wx:elif="{{item.tag == 'br'}}">
258
+      <template is="WxParseBr"></template>
259
+    </block>
260
+    <!--其他块级标签-->
261
+    <block wx:elif="{{item.tagType == 'block'}}">
262
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
263
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
264
+          <template is="wxParse3" data="{{item}}" />
265
+        </block>
266
+      </view>
267
+    </block>
268
+
269
+    <!--内联标签-->
270
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
271
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
272
+        <template is="wxParse3" data="{{item}}" />
273
+      </block>
274
+    </view>
275
+
276
+  </block>
277
+
278
+  <!--判断是否是文本节点-->
279
+  <block wx:elif="{{item.node == 'text'}}">
280
+    <!--如果是,直接进行-->
281
+    <template is="WxEmojiView" data="{{item}}" />
282
+  </block>
283
+
284
+</template>
285
+
286
+<!--循环模版-->
287
+<template name="wxParse3">
288
+  <!--<template is="wxParse4" data="{{item}}" />-->
289
+  <!--判断是否是标签节点-->
290
+  <block wx:if="{{item.node == 'element'}}">
291
+    <block wx:if="{{item.tag == 'button'}}">
292
+      <button type="default" size="mini">
293
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
294
+          <template is="wxParse4" data="{{item}}" />
295
+        </block>
296
+      </button>
297
+    </block>
298
+    <!--li类型-->
299
+    <block wx:elif="{{item.tag == 'li'}}">
300
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
301
+        <view class="{{item.classStr}} wxParse-li-inner">
302
+          <view class="{{item.classStr}} wxParse-li-text">
303
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
304
+          </view>
305
+          <view class="{{item.classStr}} wxParse-li-text">
306
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
307
+              <template is="wxParse4" data="{{item}}" />
308
+            </block>
309
+          </view>
310
+        </view>
311
+      </view>
312
+    </block>
313
+
314
+    <!--video类型-->
315
+    <block wx:elif="{{item.tag == 'video'}}">
316
+      <template is="wxParseVideo" data="{{item}}" />
317
+    </block>
318
+
319
+    <!--img类型-->
320
+    <block wx:elif="{{item.tag == 'img'}}">
321
+      <template is="wxParseImg" data="{{item}}" />
322
+    </block>
323
+
324
+    <!--a类型-->
325
+    <block wx:elif="{{item.tag == 'a'}}">
326
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
327
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
328
+          <template is="wxParse4" data="{{item}}" />
329
+        </block>
330
+      </view>
331
+    </block>
332
+
333
+    <block wx:elif="{{item.tag == 'br'}}">
334
+      <template is="WxParseBr"></template>
335
+    </block>
336
+    <!--其他块级标签-->
337
+    <block wx:elif="{{item.tagType == 'block'}}">
338
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
339
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
340
+          <template is="wxParse4" data="{{item}}" />
341
+        </block>
342
+      </view>
343
+    </block>
344
+
345
+    <!--内联标签-->
346
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
347
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
348
+        <template is="wxParse4" data="{{item}}" />
349
+      </block>
350
+    </view>
351
+
352
+  </block>
353
+
354
+  <!--判断是否是文本节点-->
355
+  <block wx:elif="{{item.node == 'text'}}">
356
+    <!--如果是,直接进行-->
357
+    <template is="WxEmojiView" data="{{item}}" />
358
+  </block>
359
+
360
+</template>
361
+
362
+<!--循环模版-->
363
+<template name="wxParse4">
364
+  <!--<template is="wxParse5" data="{{item}}" />-->
365
+  <!--判断是否是标签节点-->
366
+  <block wx:if="{{item.node == 'element'}}">
367
+    <block wx:if="{{item.tag == 'button'}}">
368
+      <button type="default" size="mini">
369
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
370
+          <template is="wxParse5" data="{{item}}" />
371
+        </block>
372
+      </button>
373
+    </block>
374
+    <!--li类型-->
375
+    <block wx:elif="{{item.tag == 'li'}}">
376
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
377
+        <view class="{{item.classStr}} wxParse-li-inner">
378
+          <view class="{{item.classStr}} wxParse-li-text">
379
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
380
+          </view>
381
+          <view class="{{item.classStr}} wxParse-li-text">
382
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
383
+              <template is="wxParse5" data="{{item}}" />
384
+            </block>
385
+          </view>
386
+        </view>
387
+      </view>
388
+    </block>
389
+
390
+    <!--video类型-->
391
+    <block wx:elif="{{item.tag == 'video'}}">
392
+      <template is="wxParseVideo" data="{{item}}" />
393
+    </block>
394
+
395
+    <!--img类型-->
396
+    <block wx:elif="{{item.tag == 'img'}}">
397
+      <template is="wxParseImg" data="{{item}}" />
398
+    </block>
399
+
400
+    <!--a类型-->
401
+    <block wx:elif="{{item.tag == 'a'}}">
402
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
403
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
404
+          <template is="wxParse5" data="{{item}}" />
405
+        </block>
406
+      </view>
407
+    </block>
408
+
409
+    <block wx:elif="{{item.tag == 'br'}}">
410
+      <template is="WxParseBr"></template>
411
+    </block>
412
+    <!--其他块级标签-->
413
+    <block wx:elif="{{item.tagType == 'block'}}">
414
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
415
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
416
+          <template is="wxParse5" data="{{item}}" />
417
+        </block>
418
+      </view>
419
+    </block>
420
+
421
+    <!--内联标签-->
422
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
423
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
424
+        <template is="wxParse5" data="{{item}}" />
425
+      </block>
426
+    </view>
427
+
428
+  </block>
429
+
430
+  <!--判断是否是文本节点-->
431
+  <block wx:elif="{{item.node == 'text'}}">
432
+    <!--如果是,直接进行-->
433
+    <template is="WxEmojiView" data="{{item}}" />
434
+  </block>
435
+
436
+</template>
437
+
438
+<!--循环模版-->
439
+<template name="wxParse5">
440
+  <!--<template is="wxParse6" data="{{item}}" />-->
441
+  <!--判断是否是标签节点-->
442
+  <block wx:if="{{item.node == 'element'}}">
443
+    <block wx:if="{{item.tag == 'button'}}">
444
+      <button type="default" size="mini">
445
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
446
+          <template is="wxParse6" data="{{item}}" />
447
+        </block>
448
+      </button>
449
+    </block>
450
+    <!--li类型-->
451
+    <block wx:elif="{{item.tag == 'li'}}">
452
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
453
+        <view class="{{item.classStr}} wxParse-li-inner">
454
+          <view class="{{item.classStr}} wxParse-li-text">
455
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
456
+          </view>
457
+          <view class="{{item.classStr}} wxParse-li-text">
458
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
459
+              <template is="wxParse6" data="{{item}}" />
460
+            </block>
461
+          </view>
462
+        </view>
463
+      </view>
464
+    </block>
465
+
466
+    <!--video类型-->
467
+    <block wx:elif="{{item.tag == 'video'}}">
468
+      <template is="wxParseVideo" data="{{item}}" />
469
+    </block>
470
+
471
+    <!--img类型-->
472
+    <block wx:elif="{{item.tag == 'img'}}">
473
+      <template is="wxParseImg" data="{{item}}" />
474
+    </block>
475
+
476
+    <!--a类型-->
477
+    <block wx:elif="{{item.tag == 'a'}}">
478
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
479
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
480
+          <template is="wxParse6" data="{{item}}" />
481
+        </block>
482
+      </view>
483
+    </block>
484
+
485
+    <block wx:elif="{{item.tag == 'br'}}">
486
+      <template is="WxParseBr"></template>
487
+    </block>
488
+    <!--其他块级标签-->
489
+    <block wx:elif="{{item.tagType == 'block'}}">
490
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
491
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
492
+          <template is="wxParse6" data="{{item}}" />
493
+        </block>
494
+      </view>
495
+    </block>
496
+
497
+    <!--内联标签-->
498
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
499
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
500
+        <template is="wxParse6" data="{{item}}" />
501
+      </block>
502
+    </view>
503
+
504
+  </block>
505
+
506
+  <!--判断是否是文本节点-->
507
+  <block wx:elif="{{item.node == 'text'}}">
508
+    <!--如果是,直接进行-->
509
+    <template is="WxEmojiView" data="{{item}}" />
510
+  </block>
511
+
512
+</template>
513
+
514
+<!--循环模版-->
515
+<template name="wxParse6">
516
+  <!--<template is="wxParse7" data="{{item}}" />-->
517
+  <!--判断是否是标签节点-->
518
+  <block wx:if="{{item.node == 'element'}}">
519
+    <block wx:if="{{item.tag == 'button'}}">
520
+      <button type="default" size="mini">
521
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
522
+          <template is="wxParse7" data="{{item}}" />
523
+        </block>
524
+      </button>
525
+    </block>
526
+    <!--li类型-->
527
+    <block wx:elif="{{item.tag == 'li'}}">
528
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
529
+        <view class="{{item.classStr}} wxParse-li-inner">
530
+          <view class="{{item.classStr}} wxParse-li-text">
531
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
532
+          </view>
533
+          <view class="{{item.classStr}} wxParse-li-text">
534
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
535
+              <template is="wxParse7" data="{{item}}" />
536
+            </block>
537
+          </view>
538
+        </view>
539
+      </view>
540
+    </block>
541
+
542
+    <!--video类型-->
543
+    <block wx:elif="{{item.tag == 'video'}}">
544
+      <template is="wxParseVideo" data="{{item}}" />
545
+    </block>
546
+
547
+    <!--img类型-->
548
+    <block wx:elif="{{item.tag == 'img'}}">
549
+      <template is="wxParseImg" data="{{item}}" />
550
+    </block>
551
+
552
+    <!--a类型-->
553
+    <block wx:elif="{{item.tag == 'a'}}">
554
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
555
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
556
+          <template is="wxParse7" data="{{item}}" />
557
+        </block>
558
+      </view>
559
+    </block>
560
+
561
+    <block wx:elif="{{item.tag == 'br'}}">
562
+      <template is="WxParseBr"></template>
563
+    </block>
564
+    <!--其他块级标签-->
565
+    <block wx:elif="{{item.tagType == 'block'}}">
566
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
567
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
568
+          <template is="wxParse7" data="{{item}}" />
569
+        </block>
570
+      </view>
571
+    </block>
572
+
573
+    <!--内联标签-->
574
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
575
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
576
+        <template is="wxParse7" data="{{item}}" />
577
+      </block>
578
+    </view>
579
+
580
+  </block>
581
+
582
+  <!--判断是否是文本节点-->
583
+  <block wx:elif="{{item.node == 'text'}}">
584
+    <!--如果是,直接进行-->
585
+    <template is="WxEmojiView" data="{{item}}" />
586
+  </block>
587
+
588
+</template>
589
+<!--循环模版-->
590
+<template name="wxParse7">
591
+  <!--<template is="wxParse8" data="{{item}}" />-->
592
+  <!--判断是否是标签节点-->
593
+  <block wx:if="{{item.node == 'element'}}">
594
+    <block wx:if="{{item.tag == 'button'}}">
595
+      <button type="default" size="mini">
596
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
597
+          <template is="wxParse8" data="{{item}}" />
598
+        </block>
599
+      </button>
600
+    </block>
601
+    <!--li类型-->
602
+    <block wx:elif="{{item.tag == 'li'}}">
603
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
604
+        <view class="{{item.classStr}} wxParse-li-inner">
605
+          <view class="{{item.classStr}} wxParse-li-text">
606
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
607
+          </view>
608
+          <view class="{{item.classStr}} wxParse-li-text">
609
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
610
+              <template is="wxParse8" data="{{item}}" />
611
+            </block>
612
+          </view>
613
+        </view>
614
+      </view>
615
+    </block>
616
+
617
+    <!--video类型-->
618
+    <block wx:elif="{{item.tag == 'video'}}">
619
+      <template is="wxParseVideo" data="{{item}}" />
620
+    </block>
621
+
622
+    <!--img类型-->
623
+    <block wx:elif="{{item.tag == 'img'}}">
624
+      <template is="wxParseImg" data="{{item}}" />
625
+    </block>
626
+
627
+    <!--a类型-->
628
+    <block wx:elif="{{item.tag == 'a'}}">
629
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
630
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
631
+          <template is="wxParse8" data="{{item}}" />
632
+        </block>
633
+      </view>
634
+    </block>
635
+
636
+    <block wx:elif="{{item.tag == 'br'}}">
637
+      <template is="WxParseBr"></template>
638
+    </block>
639
+    <!--其他块级标签-->
640
+    <block wx:elif="{{item.tagType == 'block'}}">
641
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
642
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
643
+          <template is="wxParse8" data="{{item}}" />
644
+        </block>
645
+      </view>
646
+    </block>
647
+
648
+    <!--内联标签-->
649
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
650
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
651
+        <template is="wxParse8" data="{{item}}" />
652
+      </block>
653
+    </view>
654
+
655
+  </block>
656
+
657
+  <!--判断是否是文本节点-->
658
+  <block wx:elif="{{item.node == 'text'}}">
659
+    <!--如果是,直接进行-->
660
+    <template is="WxEmojiView" data="{{item}}" />
661
+  </block>
662
+
663
+</template>
664
+
665
+<!--循环模版-->
666
+<template name="wxParse8">
667
+  <!--<template is="wxParse9" data="{{item}}" />-->
668
+  <!--判断是否是标签节点-->
669
+  <block wx:if="{{item.node == 'element'}}">
670
+    <block wx:if="{{item.tag == 'button'}}">
671
+      <button type="default" size="mini">
672
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
673
+          <template is="wxParse9" data="{{item}}" />
674
+        </block>
675
+      </button>
676
+    </block>
677
+    <!--li类型-->
678
+    <block wx:elif="{{item.tag == 'li'}}">
679
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
680
+        <view class="{{item.classStr}} wxParse-li-inner">
681
+          <view class="{{item.classStr}} wxParse-li-text">
682
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
683
+          </view>
684
+          <view class="{{item.classStr}} wxParse-li-text">
685
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
686
+              <template is="wxParse9" data="{{item}}" />
687
+            </block>
688
+          </view>
689
+        </view>
690
+      </view>
691
+    </block>
692
+
693
+    <!--video类型-->
694
+    <block wx:elif="{{item.tag == 'video'}}">
695
+      <template is="wxParseVideo" data="{{item}}" />
696
+    </block>
697
+
698
+    <!--img类型-->
699
+    <block wx:elif="{{item.tag == 'img'}}">
700
+      <template is="wxParseImg" data="{{item}}" />
701
+    </block>
702
+
703
+    <!--a类型-->
704
+    <block wx:elif="{{item.tag == 'a'}}">
705
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
706
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
707
+          <template is="wxParse9" data="{{item}}" />
708
+        </block>
709
+      </view>
710
+    </block>
711
+
712
+    <block wx:elif="{{item.tag == 'br'}}">
713
+      <template is="WxParseBr"></template>
714
+    </block>
715
+    <!--其他块级标签-->
716
+    <block wx:elif="{{item.tagType == 'block'}}">
717
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
718
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
719
+          <template is="wxParse9" data="{{item}}" />
720
+        </block>
721
+      </view>
722
+    </block>
723
+
724
+    <!--内联标签-->
725
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
726
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
727
+        <template is="wxParse9" data="{{item}}" />
728
+      </block>
729
+    </view>
730
+
731
+  </block>
732
+
733
+  <!--判断是否是文本节点-->
734
+  <block wx:elif="{{item.node == 'text'}}">
735
+    <!--如果是,直接进行-->
736
+    <template is="WxEmojiView" data="{{item}}" />
737
+  </block>
738
+
739
+</template>
740
+
741
+<!--循环模版-->
742
+<template name="wxParse9">
743
+  <!--<template is="wxParse10" data="{{item}}" />-->
744
+  <!--判断是否是标签节点-->
745
+  <block wx:if="{{item.node == 'element'}}">
746
+    <block wx:if="{{item.tag == 'button'}}">
747
+      <button type="default" size="mini">
748
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
749
+          <template is="wxParse10" data="{{item}}" />
750
+        </block>
751
+      </button>
752
+    </block>
753
+    <!--li类型-->
754
+    <block wx:elif="{{item.tag == 'li'}}">
755
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
756
+        <view class="{{item.classStr}} wxParse-li-inner">
757
+          <view class="{{item.classStr}} wxParse-li-text">
758
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
759
+          </view>
760
+          <view class="{{item.classStr}} wxParse-li-text">
761
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
762
+              <template is="wxParse10" data="{{item}}" />
763
+            </block>
764
+          </view>
765
+        </view>
766
+      </view>
767
+    </block>
768
+
769
+    <!--video类型-->
770
+    <block wx:elif="{{item.tag == 'video'}}">
771
+      <template is="wxParseVideo" data="{{item}}" />
772
+    </block>
773
+
774
+    <!--img类型-->
775
+    <block wx:elif="{{item.tag == 'img'}}">
776
+      <template is="wxParseImg" data="{{item}}" />
777
+    </block>
778
+
779
+    <!--a类型-->
780
+    <block wx:elif="{{item.tag == 'a'}}">
781
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
782
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
783
+          <template is="wxParse10" data="{{item}}" />
784
+        </block>
785
+      </view>
786
+    </block>
787
+
788
+    <block wx:elif="{{item.tag == 'br'}}">
789
+      <template is="WxParseBr"></template>
790
+    </block>
791
+    <!--其他块级标签-->
792
+    <block wx:elif="{{item.tagType == 'block'}}">
793
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
794
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
795
+          <template is="wxParse10" data="{{item}}" />
796
+        </block>
797
+      </view>
798
+    </block>
799
+
800
+    <!--内联标签-->
801
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
802
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
803
+        <template is="wxParse10" data="{{item}}" />
804
+      </block>
805
+    </view>
806
+
807
+  </block>
808
+
809
+  <!--判断是否是文本节点-->
810
+  <block wx:elif="{{item.node == 'text'}}">
811
+    <!--如果是,直接进行-->
812
+    <template is="WxEmojiView" data="{{item}}" />
813
+  </block>
814
+
815
+</template>
816
+
817
+<!--循环模版-->
818
+<template name="wxParse10">
819
+  <!--<template is="wxParse11" data="{{item}}" />-->
820
+  <!--判断是否是标签节点-->
821
+  <block wx:if="{{item.node == 'element'}}">
822
+    <block wx:if="{{item.tag == 'button'}}">
823
+      <button type="default" size="mini">
824
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
825
+          <template is="wxParse11" data="{{item}}" />
826
+        </block>
827
+      </button>
828
+    </block>
829
+    <!--li类型-->
830
+    <block wx:elif="{{item.tag == 'li'}}">
831
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
832
+        <view class="{{item.classStr}} wxParse-li-inner">
833
+          <view class="{{item.classStr}} wxParse-li-text">
834
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
835
+          </view>
836
+          <view class="{{item.classStr}} wxParse-li-text">
837
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
838
+              <template is="wxParse11" data="{{item}}" />
839
+            </block>
840
+          </view>
841
+        </view>
842
+      </view>
843
+    </block>
844
+
845
+    <!--video类型-->
846
+    <block wx:elif="{{item.tag == 'video'}}">
847
+      <template is="wxParseVideo" data="{{item}}" />
848
+    </block>
849
+
850
+    <!--img类型-->
851
+    <block wx:elif="{{item.tag == 'img'}}">
852
+      <template is="wxParseImg" data="{{item}}" />
853
+    </block>
854
+
855
+    <!--a类型-->
856
+    <block wx:elif="{{item.tag == 'a'}}">
857
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
858
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
859
+          <template is="wxParse11" data="{{item}}" />
860
+        </block>
861
+      </view>
862
+    </block>
863
+
864
+    <block wx:elif="{{item.tag == 'br'}}">
865
+      <template is="WxParseBr"></template>
866
+    </block>
867
+    <!--其他块级标签-->
868
+    <block wx:elif="{{item.tagType == 'block'}}">
869
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
870
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
871
+          <template is="wxParse11" data="{{item}}" />
872
+        </block>
873
+      </view>
874
+    </block>
875
+
876
+    <!--内联标签-->
877
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
878
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
879
+        <template is="wxParse11" data="{{item}}" />
880
+      </block>
881
+    </view>
882
+
883
+  </block>
884
+
885
+  <!--判断是否是文本节点-->
886
+  <block wx:elif="{{item.node == 'text'}}">
887
+    <!--如果是,直接进行-->
888
+    <template is="WxEmojiView" data="{{item}}" />
889
+  </block>
890
+
891
+</template>
892
+
893
+<!--循环模版-->
894
+<template name="wxParse11">
895
+  <!--<template is="wxParse12" data="{{item}}" />-->
896
+  <!--判断是否是标签节点-->
897
+  <block wx:if="{{item.node == 'element'}}">
898
+    <block wx:if="{{item.tag == 'button'}}">
899
+      <button type="default" size="mini">
900
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
901
+          <template is="wxParse12" data="{{item}}" />
902
+        </block>
903
+      </button>
904
+    </block>
905
+    <!--li类型-->
906
+    <block wx:elif="{{item.tag == 'li'}}">
907
+      <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
908
+        <view class="{{item.classStr}} wxParse-li-inner">
909
+          <view class="{{item.classStr}} wxParse-li-text">
910
+            <view class="{{item.classStr}} wxParse-li-circle"></view>
911
+          </view>
912
+          <view class="{{item.classStr}} wxParse-li-text">
913
+            <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
914
+              <template is="wxParse12" data="{{item}}" />
915
+            </block>
916
+          </view>
917
+        </view>
918
+      </view>
919
+    </block>
920
+
921
+    <!--video类型-->
922
+    <block wx:elif="{{item.tag == 'video'}}">
923
+      <template is="wxParseVideo" data="{{item}}" />
924
+    </block>
925
+
926
+    <!--img类型-->
927
+    <block wx:elif="{{item.tag == 'img'}}">
928
+      <template is="wxParseImg" data="{{item}}" />
929
+    </block>
930
+
931
+    <!--a类型-->
932
+    <block wx:elif="{{item.tag == 'a'}}">
933
+      <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}" hover-class="wxParse-a-hover">
934
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
935
+          <template is="wxParse12" data="{{item}}" />
936
+        </block>
937
+      </view>
938
+    </block>
939
+
940
+    <block wx:elif="{{item.tag == 'br'}}">
941
+      <template is="WxParseBr"></template>
942
+    </block>
943
+    <!--其他块级标签-->
944
+    <block wx:elif="{{item.tagType == 'block'}}">
945
+      <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
946
+        <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
947
+          <template is="wxParse12" data="{{item}}" />
948
+        </block>
949
+      </view>
950
+    </block>
951
+
952
+    <!--内联标签-->
953
+    <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
954
+      <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
955
+        <template is="wxParse12" data="{{item}}" />
956
+      </block>
957
+    </view>
958
+
959
+  </block>
960
+
961
+  <!--判断是否是文本节点-->
962
+  <block wx:elif="{{item.node == 'text'}}">
963
+    <!--如果是,直接进行-->
964
+    <template is="WxEmojiView" data="{{item}}" />
965
+  </block>
966
+
967
+</template>

+ 270 - 0
src/utils/wxParse/wxParse.wxss

@@ -0,0 +1,270 @@
1
+/**
2
+ * author: Di (微信小程序开发工程师)
3
+ * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
4
+ *               垂直微信小程序开发交流社区
5
+ * 
6
+ * github地址: https://github.com/icindy/wxParse
7
+ * 
8
+ * for: 微信小程序富文本解析
9
+ * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
10
+ */
11
+
12
+.wxParse {
13
+  margin: 0 5px;
14
+  font-family: Helvetica, sans-serif;
15
+  font-size: 28rpx;
16
+  color: #666;
17
+  line-height: 1.8;
18
+}
19
+view {
20
+  word-break: break-all;
21
+}
22
+.wxParse-inline {
23
+  display: inline;
24
+  margin: 0;
25
+  padding: 0;
26
+}
27
+/*//标题 */
28
+.wxParse-div {
29
+  margin: 0;
30
+  padding: 0;
31
+}
32
+.wxParse-h1 {
33
+  font-size: 2em;
34
+  margin: 0.67em 0;
35
+}
36
+.wxParse-h2 {
37
+  font-size: 1.5em;
38
+  margin: 0.75em 0;
39
+}
40
+.wxParse-h3 {
41
+  font-size: 1.17em;
42
+  margin: 0.83em 0;
43
+}
44
+.wxParse-h4 {
45
+  margin: 1.12em 0;
46
+}
47
+.wxParse-h5 {
48
+  font-size: 0.83em;
49
+  margin: 1.5em 0;
50
+}
51
+.wxParse-h6 {
52
+  font-size: 0.75em;
53
+  margin: 1.67em 0;
54
+}
55
+
56
+.wxParse-h1 {
57
+  font-size: 18px;
58
+  font-weight: 400;
59
+  margin-bottom: 0.9em;
60
+}
61
+.wxParse-h2 {
62
+  font-size: 16px;
63
+  font-weight: 400;
64
+  margin-bottom: 0.34em;
65
+}
66
+.wxParse-h3 {
67
+  font-weight: 400;
68
+  font-size: 15px;
69
+  margin-bottom: 0.34em;
70
+}
71
+.wxParse-h4 {
72
+  font-weight: 400;
73
+  font-size: 14px;
74
+  margin-bottom: 0.24em;
75
+}
76
+.wxParse-h5 {
77
+  font-weight: 400;
78
+  font-size: 13px;
79
+  margin-bottom: 0.14em;
80
+}
81
+.wxParse-h6 {
82
+  font-weight: 400;
83
+  font-size: 12px;
84
+  margin-bottom: 0.04em;
85
+}
86
+
87
+.wxParse-h1,
88
+.wxParse-h2,
89
+.wxParse-h3,
90
+.wxParse-h4,
91
+.wxParse-h5,
92
+.wxParse-h6,
93
+.wxParse-b,
94
+.wxParse-strong {
95
+  font-weight: bolder;
96
+}
97
+
98
+.wxParse-i,
99
+.wxParse-cite,
100
+.wxParse-em,
101
+.wxParse-var,
102
+.wxParse-address {
103
+  font-style: italic;
104
+}
105
+.wxParse-pre,
106
+.wxParse-tt,
107
+.wxParse-code,
108
+.wxParse-kbd,
109
+.wxParse-samp {
110
+  font-family: monospace;
111
+}
112
+.wxParse-pre {
113
+  white-space: pre;
114
+}
115
+.wxParse-big {
116
+  font-size: 1.17em;
117
+}
118
+.wxParse-small,
119
+.wxParse-sub,
120
+.wxParse-sup {
121
+  font-size: 0.83em;
122
+}
123
+.wxParse-sub {
124
+  vertical-align: sub;
125
+}
126
+.wxParse-sup {
127
+  vertical-align: super;
128
+}
129
+.wxParse-s,
130
+.wxParse-strike,
131
+.wxParse-del {
132
+  text-decoration: line-through;
133
+}
134
+/*wxparse-自定义个性化的css样式*/
135
+/*增加video的css样式*/
136
+.wxParse-strong,
137
+.wxParse-s {
138
+  display: inline;
139
+}
140
+.wxParse-a {
141
+  color: #576b95;
142
+  text-decoration: underline;
143
+  word-break: break-all;
144
+  overflow: auto;
145
+}
146
+
147
+.wxParse-a-hover {
148
+  opacity: 0.8;
149
+}
150
+
151
+.wxParse-video {
152
+  text-align: center;
153
+  margin: 10px 0;
154
+}
155
+
156
+.wxParse-video-video {
157
+  width: 100%;
158
+}
159
+
160
+.wxParse-img {
161
+  /*background-color: #efefef;*/
162
+  overflow: hidden;
163
+}
164
+
165
+.wxParse-blockquote {
166
+  margin: 0;
167
+  padding: 10px 0 10px 5px;
168
+  font-family: Courier, Calibri, '宋体';
169
+  background: #f5f5f5;
170
+  border-left: 3px solid #dbdbdb;
171
+}
172
+
173
+.wxParse-code,
174
+.wxParse-wxxxcode-style {
175
+  display: inline;
176
+  background: #f5f5f5;
177
+}
178
+.wxParse-ul {
179
+  margin: 20rpx 10rpx;
180
+}
181
+
182
+.wxParse-li,
183
+.wxParse-li-inner {
184
+  display: flex;
185
+  align-items: baseline;
186
+}
187
+.wxParse-li-text {
188
+  align-items: center;
189
+}
190
+
191
+.wxParse-li-circle {
192
+  display: inline-flex;
193
+  width: 6px;
194
+  height: 6px;
195
+  border-radius: 3px;
196
+  background-color: #333;
197
+  margin-right: 5px;
198
+}
199
+
200
+.wxParse-li-square {
201
+  display: inline-flex;
202
+  width: 10rpx;
203
+  height: 10rpx;
204
+  background-color: #333;
205
+  margin-right: 5px;
206
+}
207
+.wxParse-li-ring {
208
+  display: inline-flex;
209
+  width: 10rpx;
210
+  height: 10rpx;
211
+  border: 2rpx solid #333;
212
+  border-radius: 50%;
213
+  background-color: #fff;
214
+  margin-right: 5px;
215
+}
216
+
217
+/*.wxParse-table{
218
+    width: 100%;
219
+    height: 400px;
220
+}
221
+.wxParse-thead,.wxParse-tfoot,.wxParse-tr{
222
+    display: flex;
223
+    flex-direction: row;
224
+}
225
+.wxParse-th,.wxParse-td{
226
+    display: flex;
227
+    width: 580px;
228
+    overflow: auto;
229
+}*/
230
+
231
+.wxParse-u {
232
+  text-decoration: underline;
233
+}
234
+.wxParse-hide {
235
+  display: none;
236
+}
237
+.WxEmojiView {
238
+  align-items: center;
239
+}
240
+.wxEmoji {
241
+  width: 16px;
242
+  height: 16px;
243
+}
244
+.wxParse-tr {
245
+  display: flex;
246
+  border-right: 1px solid #e0e0e0;
247
+  border-bottom: 1px solid #e0e0e0;
248
+  border-top: 1px solid #e0e0e0;
249
+}
250
+.wxParse-th,
251
+.wxParse-td {
252
+  flex: 1;
253
+  padding: 5px;
254
+  font-size: 28rpx;
255
+  border-left: 1px solid #e0e0e0;
256
+  word-break: break-all;
257
+}
258
+.wxParse-td:last {
259
+  border-top: 1px solid #e0e0e0;
260
+}
261
+.wxParse-th {
262
+  background: #f0f0f0;
263
+  border-top: 1px solid #e0e0e0;
264
+}
265
+.wxParse-del {
266
+  display: inline;
267
+}
268
+.wxParse-figure {
269
+  overflow: hidden;
270
+}

+ 10 - 0
src/wxs/stringFilter.wxs

@@ -0,0 +1,10 @@
1
+var filter = function (text) {
2
+  if (text) {
3
+    var pattern = '\\\\n';
4
+    var target = '\n';
5
+    var reg = getRegExp(pattern, 'g');
6
+    return text.replace(reg, target);
7
+  }
8
+};
9
+
10
+module.exports.filter = filter;

modify message relative apis · 165424743a - Gogs: Go Git Service

modify message relative apis

Brightcells лет %!s(int64=9): %!d(string=назад)
Родитель
Сommit
165424743a

+ 3 - 4
api/urls.py

@@ -49,10 +49,9 @@ urlpatterns += [
49 49
 # 消息相关
50 50
 urlpatterns += [
51 51
     url(r'^msg/list$', message_views.message_list_api, name='message_list_api'),  # 消息列表
52
-    url(r'^msg/list/(?P<msg_type>\w+)$', message_views.message_type_list_api, name='message_type_list_api'),  # 分类消息列表
53
-    url(r'^msg/read$', message_views.message_read_api, name='message_read_api'),  # 消息读取
54
-    url(r'^msg/delete$', message_views.message_delete_api, name='message_delete_api'),  # 消息删除
55
-    url(r'^msg/delete/(?P<msg_type>\w+)$', message_views.message_delete_api, name='message_type_delete_api'),  # 消息删除
52
+    url(r'^msg/(?P<msg_type>\w+)/list$', message_views.message_type_list_api, name='message_type_list_api'),  # 分类消息列表
53
+    url(r'^msg/(?P<msg_type>\w+)/read$', message_views.message_type_read_api, name='message_type_read_api'),  # 消息读取
54
+    url(r'^msg/(?P<msg_type>\w+)/delete$', message_views.message_type_delete_api, name='message_type_delete_api'),  # 消息删除
56 55
 ]
57 56
 
58 57
 # 控制器相关

+ 19 - 1
message/admin.py

@@ -2,7 +2,7 @@
2 2
 
3 3
 from django.contrib import admin
4 4
 
5
-from message.models import UserMessageInfo
5
+from message.models import UserMessageInfo, SystemMessageInfo, SystemMessageReadInfo, SystemMessageDeleteInfo
6 6
 
7 7
 
8 8
 class UserMessageInfoAdmin(admin.ModelAdmin):
@@ -10,4 +10,22 @@ class UserMessageInfoAdmin(admin.ModelAdmin):
10 10
     list_filter = ('msg_type', 'read', 'status')
11 11
 
12 12
 
13
+class SystemMessageInfoAdmin(admin.ModelAdmin):
14
+    list_display = ('title', 'content', 'url', 'status', 'created_at', 'updated_at')
15
+    list_filter = ('status', )
16
+
17
+
18
+class SystemMessageReadInfoAdmin(admin.ModelAdmin):
19
+    list_display = ('user_id', 'msg_id', 'status', 'created_at', 'updated_at')
20
+    list_filter = ('status', )
21
+
22
+
23
+class SystemMessageDeleteInfoAdmin(admin.ModelAdmin):
24
+    list_display = ('user_id', 'msg_id', 'status', 'created_at', 'updated_at')
25
+    list_filter = ('status', )
26
+
27
+
13 28
 admin.site.register(UserMessageInfo, UserMessageInfoAdmin)
29
+admin.site.register(SystemMessageInfo, SystemMessageInfoAdmin)
30
+admin.site.register(SystemMessageReadInfo, SystemMessageReadInfoAdmin)
31
+admin.site.register(SystemMessageDeleteInfo, SystemMessageDeleteInfoAdmin)

+ 45 - 0
message/migrations/0003_systemmessageinfo_systemmessagereadinfo.py

@@ -0,0 +1,45 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.db import models, migrations
5
+
6
+
7
+class Migration(migrations.Migration):
8
+
9
+    dependencies = [
10
+        ('message', '0002_auto_20160120_1830'),
11
+    ]
12
+
13
+    operations = [
14
+        migrations.CreateModel(
15
+            name='SystemMessageInfo',
16
+            fields=[
17
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18
+                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', db_index=True, verbose_name='status')),
19
+                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)),
20
+                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)),
21
+                ('title', models.CharField(help_text='\u7cfb\u7edf\u6d88\u606f\u6807\u9898', max_length=255, verbose_name='title')),
22
+                ('content', models.TextField(help_text='\u7cfb\u7edf\u6d88\u606f\u5185\u5bb9', null=True, verbose_name='content', blank=True)),
23
+                ('url', models.CharField(help_text='\u7cfb\u7edf\u6d88\u606f\u94fe\u63a5', max_length=255, null=True, verbose_name='url', blank=True)),
24
+            ],
25
+            options={
26
+                'verbose_name': 'systemmessageinfo',
27
+                'verbose_name_plural': 'systemmessageinfo',
28
+            },
29
+        ),
30
+        migrations.CreateModel(
31
+            name='SystemMessageReadInfo',
32
+            fields=[
33
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
34
+                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', db_index=True, verbose_name='status')),
35
+                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)),
36
+                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)),
37
+                ('user_id', models.CharField(max_length=255, blank=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='user_id', db_index=True)),
38
+                ('msg_id', models.CharField(max_length=255, blank=True, help_text='\u7cfb\u7edf\u6d88\u606f\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='msg_id', db_index=True)),
39
+            ],
40
+            options={
41
+                'verbose_name': 'systemmessagereadinfo',
42
+                'verbose_name_plural': 'systemmessagereadinfo',
43
+            },
44
+        ),
45
+    ]

+ 29 - 0
message/migrations/0004_systemmessagedeleteinfo.py

@@ -0,0 +1,29 @@
1
+# -*- coding: utf-8 -*-
2
+from __future__ import unicode_literals
3
+
4
+from django.db import models, migrations
5
+
6
+
7
+class Migration(migrations.Migration):
8
+
9
+    dependencies = [
10
+        ('message', '0003_systemmessageinfo_systemmessagereadinfo'),
11
+    ]
12
+
13
+    operations = [
14
+        migrations.CreateModel(
15
+            name='SystemMessageDeleteInfo',
16
+            fields=[
17
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18
+                ('status', models.BooleanField(default=True, help_text='\u72b6\u6001', db_index=True, verbose_name='status')),
19
+                ('created_at', models.DateTimeField(help_text='\u521b\u5efa\u65f6\u95f4', verbose_name='created_at', auto_now_add=True)),
20
+                ('updated_at', models.DateTimeField(help_text='\u66f4\u65b0\u65f6\u95f4', verbose_name='updated_at', auto_now=True)),
21
+                ('user_id', models.CharField(max_length=255, blank=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='user_id', db_index=True)),
22
+                ('msg_id', models.CharField(max_length=255, blank=True, help_text='\u7cfb\u7edf\u6d88\u606f\u552f\u4e00\u6807\u8bc6', null=True, verbose_name='msg_id', db_index=True)),
23
+            ],
24
+            options={
25
+                'verbose_name': 'systemmessagedeleteinfo',
26
+                'verbose_name_plural': 'systemmessagedeleteinfo',
27
+            },
28
+        ),
29
+    ]

+ 47 - 2
message/models.py

@@ -57,8 +57,7 @@ class UserMessageInfo(CreateUpdateMixin):
57 57
     def __unicode__(self):
58 58
         return unicode(self.pk)
59 59
 
60
-    @property
61
-    def msg_info(self):
60
+    def msg_info(self, user_id=None):
62 61
         try:
63 62
             group_photo = GroupPhotoInfo.objects.get(pk=self.photo_id)
64 63
         except GroupPhotoInfo.DoesNotExist:
@@ -79,3 +78,49 @@ class UserMessageInfo(CreateUpdateMixin):
79 78
             'read': self.read,
80 79
             'created_at': self.created_at.replace(microsecond=0),
81 80
         }
81
+
82
+
83
+class SystemMessageInfo(CreateUpdateMixin):
84
+    title = models.CharField(_(u'title'), max_length=255, help_text=u'系统消息标题')
85
+    content = models.TextField(_(u'content'), blank=True, null=True, help_text=u'系统消息内容')
86
+    url = models.CharField(_(u'url'), max_length=255, blank=True, null=True, help_text=u'系统消息链接')
87
+
88
+    class Meta:
89
+        verbose_name = _('systemmessageinfo')
90
+        verbose_name_plural = _('systemmessageinfo')
91
+
92
+    def __unicode__(self):
93
+        return unicode(self.pk)
94
+
95
+    def msg_info(self, user_id=None):
96
+        return {
97
+            'pk': self.pk,
98
+            'title': self.title,
99
+            'content': self.content,
100
+            'url': self.url,
101
+            'read': SystemMessageReadInfo.objects.filter(user_id=user_id, msg_id=self.pk).exists(),
102
+        }
103
+
104
+
105
+class SystemMessageReadInfo(CreateUpdateMixin):
106
+    user_id = models.CharField(_(u'user_id'), max_length=255, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
107
+    msg_id = models.CharField(_(u'msg_id'), max_length=255, blank=True, null=True, help_text=u'系统消息唯一标识', db_index=True)
108
+
109
+    class Meta:
110
+        verbose_name = _('systemmessagereadinfo')
111
+        verbose_name_plural = _('systemmessagereadinfo')
112
+
113
+    def __unicode__(self):
114
+        return unicode(self.pk)
115
+
116
+
117
+class SystemMessageDeleteInfo(CreateUpdateMixin):
118
+    user_id = models.CharField(_(u'user_id'), max_length=255, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
119
+    msg_id = models.CharField(_(u'msg_id'), max_length=255, blank=True, null=True, help_text=u'系统消息唯一标识', db_index=True)
120
+
121
+    class Meta:
122
+        verbose_name = _('systemmessagedeleteinfo')
123
+        verbose_name_plural = _('systemmessagedeleteinfo')
124
+
125
+    def __unicode__(self):
126
+        return unicode(self.pk)

+ 82 - 24
message/views.py

@@ -3,13 +3,16 @@
3 3
 from django.conf import settings
4 4
 from django.http import JsonResponse
5 5
 
6
-from message.models import UserMessageInfo
6
+from account.models import UserInfo
7
+from message.models import UserMessageInfo, SystemMessageInfo, SystemMessageReadInfo, SystemMessageDeleteInfo
7 8
 
8 9
 from utils.page_utils import pagination
9 10
 
10
-from utils.error.errno_utils import MessageStatusCode
11
+from utils.error.errno_utils import UserStatusCode, MessageStatusCode
11 12
 from utils.error.response_utils import response
12 13
 
14
+from utils.redis.rmessage import set_system_message_delete_info, get_system_message_delete_info
15
+
13 16
 
14 17
 def message_list_api(request):
15 18
     """
@@ -23,14 +26,24 @@ def message_list_api(request):
23 26
 
24 27
     final_messages = []
25 28
     for message in messages:
26
-        type_messages = UserMessageInfo.objects.filter(
27
-            to_uid=user_id,
28
-            msg_type=message['msg_type'],
29
-            status=True,
30
-        ).order_by(
29
+        msg_type = message['msg_type']
30
+        if msg_type == UserMessageInfo.SYSTEM:
31
+            deleted_message_ids = get_system_message_delete_info(user_id)
32
+            type_messages = SystemMessageInfo.objects.filter(
33
+                status=True,
34
+            ).exclude(
35
+                pk__in=deleted_message_ids,
36
+            )
37
+        else:
38
+            type_messages = UserMessageInfo.objects.filter(
39
+                to_uid=user_id,
40
+                msg_type=msg_type,
41
+                status=True,
42
+            )
43
+        type_messages = type_messages.order_by(
31 44
             '-pk'
32 45
         )[:settings.MESSAGE_NUM_PER_PAGE]
33
-        type_messages = [msg.msg_info for msg in type_messages]
46
+        type_messages = [msg.msg_info(user_id) for msg in type_messages]
34 47
         message['msg_list'] = type_messages
35 48
         final_messages.append(message)
36 49
 
@@ -54,15 +67,24 @@ def message_type_list_api(request, msg_type):
54 67
     page = int(request.POST.get('page', 0) or request.GET.get('page', 0) or 1)
55 68
     num = int(request.POST.get('num', 0) or request.GET.get('num', 0) or settings.MESSAGE_NUM_PER_PAGE)
56 69
 
57
-    type_messages = UserMessageInfo.objects.filter(
58
-        to_uid=user_id,
59
-        msg_type=msg_type,
60
-        status=True,
61
-    ).order_by(
70
+    if msg_type == UserMessageInfo.SYSTEM:
71
+        deleted_message_ids = get_system_message_delete_info(user_id)
72
+        type_messages = SystemMessageInfo.objects.filter(
73
+            status=True,
74
+        ).exclude(
75
+            pk__in=deleted_message_ids,
76
+        )
77
+    else:
78
+        type_messages = UserMessageInfo.objects.filter(
79
+            to_uid=user_id,
80
+            msg_type=msg_type,
81
+            status=True,
82
+        )
83
+    type_messages = type_messages.order_by(
62 84
         '-pk'
63 85
     )
64 86
     type_messages, left = pagination(type_messages, page, num)
65
-    type_messages = [msg.msg_info for msg in type_messages]
87
+    type_messages = [msg.msg_info(user_id) for msg in type_messages]
66 88
 
67 89
     return JsonResponse({
68 90
         'status': 200,
@@ -74,16 +96,35 @@ def message_type_list_api(request, msg_type):
74 96
     })
75 97
 
76 98
 
77
-def message_read_api(request):
99
+def message_type_read_api(request, msg_type=None):
78 100
     """
79 101
     消息读取
80 102
     :param request:
81 103
     :return:
82 104
     """
83
-    pk = int(request.POST.get('pk', 0) or request.GET.get('pk', 0) or -1)
84 105
     user_id = request.POST.get('user_id', '') or request.GET.get('user_id', '')
85
-
86
-    UserMessageInfo.objects.filter(pk=pk, to_uid=user_id).update(read=True)
106
+    pk = int(request.POST.get('pk', 0) or request.GET.get('pk', 0) or -1)
107
+    _all = request.POST.get('all', '') or request.GET.get('all', '')
108
+
109
+    # 用户校验
110
+    try:
111
+        user = UserInfo.objects.get(user_id=user_id)
112
+    except UserInfo.DoesNotExist:
113
+        return response(UserStatusCode.USER_NOT_FOUND)
114
+
115
+    if msg_type == UserMessageInfo.SYSTEM:
116
+        if pk > 0:  # 系统消息单个读取
117
+            if not SystemMessageInfo.objects.filter(pk=pk).exists():
118
+                return response(MessageStatusCode.MESSAGE_NOT_FOUND)
119
+            SystemMessageReadInfo.objects.get_or_create(user_id=user_id, msg_id=pk)
120
+        if _all:  # 系统消息全部读取
121
+            for msg in SystemMessageInfo.objects.filter(status=True):
122
+                SystemMessageReadInfo.objects.get_or_create(user_id=user_id, msg_id=msg.pk)
123
+    else:
124
+        if pk > 0:  # 用户(点赞/评论)消息单个读取
125
+            UserMessageInfo.objects.filter(pk=pk, to_uid=user_id).update(read=True)
126
+        if _all:  # 用户消息(点赞/评论)全部读取
127
+            UserMessageInfo.objects.filter(to_uid=user_id, msg_type=msg_type).update(read=True)
87 128
 
88 129
     return JsonResponse({
89 130
         'status': 200,
@@ -91,7 +132,7 @@ def message_read_api(request):
91 132
     })
92 133
 
93 134
 
94
-def message_delete_api(request, msg_type=None):
135
+def message_type_delete_api(request, msg_type=None):
95 136
     """
96 137
     消息删除
97 138
     :param request:
@@ -99,12 +140,29 @@ def message_delete_api(request, msg_type=None):
99 140
     :return:
100 141
     """
101 142
     user_id = request.POST.get('user_id', '') or request.GET.get('user_id', '')
102
-
103
-    if msg_type:
104
-        UserMessageInfo.objects.filter(to_uid=user_id, msg_type=msg_type).update(status=False)
143
+    pk = int(request.POST.get('pk', 0) or request.GET.get('pk', 0) or -1)
144
+    _all = request.POST.get('all', '') or request.GET.get('all', '')
145
+
146
+    # 用户校验
147
+    try:
148
+        user = UserInfo.objects.get(user_id=user_id)
149
+    except UserInfo.DoesNotExist:
150
+        return response(UserStatusCode.USER_NOT_FOUND)
151
+
152
+    if msg_type == UserMessageInfo.SYSTEM:
153
+        if pk > 0:  # 系统消息单个删除
154
+            if not SystemMessageInfo.objects.filter(pk=pk).exists():
155
+                return response(MessageStatusCode.MESSAGE_NOT_FOUND)
156
+            SystemMessageDeleteInfo.objects.get_or_create(user_id=user_id, msg_id=pk)
157
+        if _all:  # 系统消息全部删除
158
+            for msg in SystemMessageInfo.objects.filter(status=True):
159
+                SystemMessageDeleteInfo.objects.get_or_create(user_id=user_id, msg_id=msg.pk)
160
+        set_system_message_delete_info(user_id)
105 161
     else:
106
-        pk = int(request.POST.get('pk', 0) or request.GET.get('pk', 0) or -1)
107
-        UserMessageInfo.objects.filter(pk=pk, to_uid=user_id).update(status=False)
162
+        if pk > 0:  # 用户(点赞/评论)消息单个删除
163
+            UserMessageInfo.objects.filter(pk=pk, to_uid=user_id).update(status=False)
164
+        if _all:  # 用户消息(点赞/评论)全部删除
165
+            UserMessageInfo.objects.filter(to_uid=user_id, msg_type=msg_type).update(status=False)
108 166
 
109 167
     return JsonResponse({
110 168
         'status': 200,

+ 4 - 1
utils/redis/rkeys.py

@@ -1,7 +1,7 @@
1 1
 # -*- coding: utf-8 -*-
2 2
 
3 3
 # 用户相关
4
-PROFILE_INFO = 'profile:info:%s'  # STRING,用户信息,uid
4
+PROFILE_INFO = 'profile:info:%s'  # STRING,用户信息,user_id
5 5
 
6 6
 # 群组相关
7 7
 GROUP_INFO = 'group:info:%s'  # STRING,群组信息,group_id
@@ -16,3 +16,6 @@ GROUP_USERS_QUIT_SET = 'group:users:quit:set:%s'  # SET,群组用户退出集
16 16
 
17 17
 # 群组照片相关
18 18
 GROUP_LAST_PHOTO_PK = 'group:last:photo:pk:%s'  # STRING,群组最后一张照片PK,group_id
19
+
20
+# 系统消息相关
21
+SYSTEM_MESSAGE_DELETED_INFO = 'system:message:deleted:info:%s'  # STRING,系统消息删除信息,user_id

+ 28 - 0
utils/redis/rmessage.py

@@ -0,0 +1,28 @@
1
+# -*- coding: utf-8 -*-
2
+
3
+from django.conf import settings
4
+
5
+from message.models import SystemMessageDeleteInfo
6
+
7
+from utils.redis.rkeys import SYSTEM_MESSAGE_DELETED_INFO
8
+
9
+import json
10
+
11
+
12
+r = settings.REDIS_CACHE
13
+
14
+
15
+# 系统消息相关
16
+
17
+
18
+def set_system_message_delete_info(user_id):
19
+    """ 设置系统消息删除信息 """
20
+    deleted_messages = SystemMessageDeleteInfo.objects.filter(user_id=user_id, status=True)
21
+    deleted_message_ids = [msg.msg_id for msg in deleted_messages]
22
+    r.set(SYSTEM_MESSAGE_DELETED_INFO % user_id, json.dumps(deleted_message_ids))
23
+    return deleted_message_ids
24
+
25
+
26
+def get_system_message_delete_info(user_id):
27
+    """ 获取系统消息删除信息 """
28
+    return json.loads(r.get(SYSTEM_MESSAGE_DELETED_INFO % user_id) or '[]') or set_system_message_delete_info(user_id)

Kodo/kodo - Gogs: Go Git Service

4 Commity (58c38a0546bc7a24108ca535a640913ef559bc03)

Autor SHA1 Wiadomość Data
  Brightcells f72ccb1875 isort import 9 lat temu
  Brightcells f587f0a48f user can just see photo after he joined the group & zh_Hans replace zh_CN in settings.py 10 lat temu
  Brightcells 7e305483ab add api group_detail/group_lock/group_pass/group_refuse 10 lat temu
  Brightcells e184e7dd5b add api group_create/group_join 10 lat temu