From 87890a10ed786f85677f83336771aa97dcb252d7 Mon Sep 17 00:00:00 2001 From: weicw Date: Sat, 8 Aug 2026 06:48:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai-house):=20=E6=94=AF=E6=8C=81=20Markdown?= =?UTF-8?q?=20=E5=B9=B6=E4=BC=98=E5=8C=96=E6=89=BE=E6=88=BF=E4=BA=A4?= =?UTF-8?q?=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.js | 4 +- pages/ai-house/index.vue | 219 ++---------------- tests/check-ai-house-location-response.js | 64 ++--- tests/check-ai-house-markdown.js | 24 ++ tests/check-ai-house-quick-questions.js | 24 ++ .../components/mp-html/markdown/index.js | 34 +++ .../components/mp-html/markdown/marked.min.js | 6 + .../mp-html/components/mp-html/mp-html.vue | 4 +- 8 files changed, 138 insertions(+), 241 deletions(-) create mode 100644 tests/check-ai-house-markdown.js create mode 100644 tests/check-ai-house-quick-questions.js create mode 100644 uni_modules/mp-html/components/mp-html/markdown/index.js create mode 100644 uni_modules/mp-html/components/mp-html/markdown/marked.min.js diff --git a/config.js b/config.js index b8ca12b..4d253f4 100755 --- a/config.js +++ b/config.js @@ -10,8 +10,8 @@ module.exports = { // 生产环境 serverUrl: 'https://server.websoft.top/api', - // apiUrl: 'http://127.0.0.1:9200/api', - apiUrl: 'https://cms-api.websoft.top/api', + apiUrl: 'http://127.0.0.1:9200/api', + // apiUrl: 'https://cms-api.websoft.top/api', socketUrl: 'wss://server.websoft.top', fileUrl: 'https://file.wsdns.cn', diff --git a/pages/ai-house/index.vue b/pages/ai-house/index.vue index 0cfa67c..f75ddc0 100644 --- a/pages/ai-house/index.vue +++ b/pages/ai-house/index.vue @@ -2,14 +2,6 @@ AI找房助手 - - - 清空 - @@ -41,7 +33,7 @@ > - {{ message.content || '您好,请填写您的找房需求' }} + 填写楼盘、面积和预算,我来帮您筛选合适的房源。 楼盘 @@ -118,7 +110,12 @@ - {{ message.content }} + + {{ message.content }} @@ -132,25 +129,6 @@ - - {{ message.content }} - - {{ item.locationName }} - - {{ [item.city, item.parentLocationName].filter(Boolean).join(' / ') }} - - - {{ tag }} - - - {{ knowledge.title }} - - {{ locationKnowledgeFacts(knowledge) }} - - {{ knowledge.content }} - - - @@ -218,6 +196,14 @@ > {{ item.label }} + + + 清空 + { - if (!item || typeof item !== 'object') { - return null - } - return { - ...item, - locationId: item.locationId ?? item.location_id ?? `location-${index}`, - locationName: item.locationName ?? item.location_name ?? item.name ?? '地点资料', - parentLocationName: item.parentLocationName ?? item.parent_location_name ?? '', - tags: this.normalizeLocationTags(item.tags), - knowledgeItems: this.normalizeLocationKnowledgeItems( - item.knowledgeItems !== undefined ? item.knowledgeItems : item.knowledge_items - ) - } - }) - .filter(Boolean) - }, - normalizeStructuredArray(value) { - if (Array.isArray(value)) { - return value - } - if (typeof value === 'string') { - try { - const parsed = JSON.parse(value) - return Array.isArray(parsed) ? parsed : (parsed && typeof parsed === 'object' ? [parsed] : []) - } catch (error) { - return [] - } - } - return value && typeof value === 'object' ? [value] : [] - }, - normalizeLocationTags(value) { - if (Array.isArray(value)) { - return value.filter(Boolean).map((item) => String(item)) - } - if (typeof value === 'string') { - const tags = this.normalizeStructuredArray(value) - if (tags.length) { - return tags.filter(Boolean).map((item) => String(item)) - } - return value.split(/[,,]/).map((item) => item.trim()).filter(Boolean) - } - return [] - }, - normalizeLocationKnowledgeItems(value) { - return this.normalizeStructuredArray(value) - .map((item, index) => { - if (!item || typeof item !== 'object') { - return null - } - return { - ...item, - topic: item.topic || `knowledge-${index}`, - name: item.name || '', - title: item.title || '补充信息', - propertyCompany: item.propertyCompany ?? item.property_company, - propertyFees: item.propertyFees ?? item.property_fees, - waterBillingType: item.waterBillingType ?? item.water_billing_type, - waterUnitPrice: item.waterUnitPrice ?? item.water_unit_price, - electricityBillingType: item.electricityBillingType ?? item.electricity_billing_type, - electricityUnitPrice: item.electricityUnitPrice ?? item.electricity_unit_price, - parkingAvailable: this.normalizeBoolean(item.parkingAvailable ?? item.parking_available), - parkingFee: item.parkingFee ?? item.parking_fee - } - }) - .filter(Boolean) - }, - normalizeBoolean(value) { - if (value === 'true' || value === 1 || value === '1') { - return true - } - if (value === 'false' || value === 0 || value === '0') { - return false - } - return value - }, connectWebSocket() { if (!this.loginUserId) { return @@ -847,17 +755,6 @@ }) ) } - const locationCards = this.normalizeLocationCards( - data.locationCards !== undefined ? data.locationCards : data.location_cards - ) - if (locationCards.length) { - this.messages.push( - this.createMessage('ai', 'location', { - content: '以下地点资料来自已维护的知识库:', - locationCards - }) - ) - } if (data.showContactForm) { this.messages.push(this.createMessage('ai', 'messageForm')) } @@ -872,19 +769,6 @@ url: `/sub_pages/house/detail?houseId=${item.houseId}` }) }, - locationKnowledgeFacts(item) { - const facts = [] - if (item.propertyCompany) facts.push(`物业公司:${item.propertyCompany}`) - if (item.propertyFees !== null && item.propertyFees !== undefined) facts.push(`物业费:${item.propertyFees}`) - if (item.waterBillingType) facts.push(`水费:${item.waterBillingType}`) - if (item.waterUnitPrice !== null && item.waterUnitPrice !== undefined) facts.push(`水费单价:${item.waterUnitPrice}`) - if (item.electricityBillingType) facts.push(`电费:${item.electricityBillingType}`) - if (item.electricityUnitPrice !== null && item.electricityUnitPrice !== undefined) facts.push(`电费单价:${item.electricityUnitPrice}`) - if (item.parkingAvailable === true) facts.push('停车:可用') - if (item.parkingAvailable === false) facts.push('停车:不可用') - if (item.parkingFee) facts.push(`停车费用:${item.parkingFee}`) - return facts.join(';') - }, scrollToBottom() { this.$nextTick(() => { setTimeout(() => { @@ -939,6 +823,10 @@ opacity: 0.45; } + .clear-session-btn-inline { + margin-left: auto; + } + .message-scroll { padding: 24rpx 24rpx 0; box-sizing: border-box; @@ -1055,69 +943,6 @@ justify-content: flex-start; } - .location-message { - padding: 20rpx; - border-radius: 24rpx; - background: #f8fbff; - border: 1rpx solid #e6eefc; - } - - .location-card { - margin-top: 16rpx; - padding: 20rpx; - border-radius: 16rpx; - background: #ffffff; - border: 1rpx solid #e9edf5; - } - - .location-card-title { - font-size: 30rpx; - font-weight: 600; - color: #1f2937; - } - - .location-card-path { - margin-top: 8rpx; - font-size: 23rpx; - color: #667085; - } - - .location-tag-row { - display: flex; - flex-wrap: wrap; - gap: 8rpx; - margin-top: 14rpx; - } - - .location-tag { - padding: 4rpx 12rpx; - border-radius: 8rpx; - background: #eef6ff; - color: #2f5f9f; - font-size: 21rpx; - } - - .location-knowledge-item { - margin-top: 16rpx; - padding-top: 16rpx; - border-top: 1rpx solid #eef1f6; - } - - .location-knowledge-title { - font-size: 26rpx; - font-weight: 600; - color: #344054; - } - - .location-knowledge-facts, - .location-knowledge-content { - margin-top: 8rpx; - font-size: 24rpx; - line-height: 1.55; - color: #667085; - word-break: break-all; - } - .faq-message { padding: 8rpx 0 0; } diff --git a/tests/check-ai-house-location-response.js b/tests/check-ai-house-location-response.js index 9fa5991..22eb9f8 100644 --- a/tests/check-ai-house-location-response.js +++ b/tests/check-ai-house-location-response.js @@ -29,47 +29,29 @@ function createPageVm() { return vm } -function receiveLocationResponse(fieldName, locationCards) { - const vm = createPageVm() - const response = { - type: 'house_ai_result', - answer: '已按已维护的地点资料筛选,以下地点可供参考。' - } - response[fieldName] = locationCards - vm.handleSocketMessage(JSON.stringify(response)) - return vm.messages.find((message) => message.type === 'location') -} - -const locationCard = { - location_id: 101, - city: '南宁市', - location_name: '五象航洋城', - parent_location_name: '五象新区', - tags: ['停车方便'], - knowledge_items: [{ - topic: 'parking', - title: '停车', - parking_available: true, - parking_fee: '月租 300 元' +const vm = createPageVm() +vm.handleSocketMessage(JSON.stringify({ + type: 'house_ai_result', + answer: '航洋城附近通勤方便,停车以现场政策为准。', + locationCards: [{ + locationId: 101, + locationName: '航洋城', + knowledgeItems: [{ title: '停车' }] + }], + houses: [{ + houseId: 201, + houseTitle: '航洋城附近两居室', + files: [] }] -} +})) -const locationMessage = receiveLocationResponse('location_cards', JSON.stringify([locationCard])) +assert.equal(vm.messages.filter((message) => message.type === 'location').length, 0, + '地点资料不应在小程序中创建卡片消息') +assert.equal(vm.messages.filter((message) => message.type === 'text').length, 1, + '地点资料应由 AI 直接组织为文本回答') +assert.equal(vm.messages.find((message) => message.type === 'text').content, + '航洋城附近通勤方便,停车以现场政策为准。') +assert.equal(vm.messages.filter((message) => message.type === 'list').length, 1, + '房源结果仍应创建房源卡片消息') -assert.ok(locationMessage, '地点咨询回包应创建地点卡片消息') -assert.ok(Array.isArray(locationMessage.locationCards), '地点卡片应转换为数组') -assert.equal(locationMessage.locationCards[0].locationId, 101) -assert.equal(locationMessage.locationCards[0].locationName, '五象航洋城') -assert.equal(locationMessage.locationCards[0].knowledgeItems[0].parkingAvailable, true) - -const backendLocationMessage = receiveLocationResponse('locationCards', [{ - locationId: 102, - city: '南宁市', - locationName: '会展航洋城', - knowledgeItems: [] -}]) - -assert.ok(backendLocationMessage, '后端驼峰地点卡片回包应保持兼容') -assert.equal(backendLocationMessage.locationCards[0].locationName, '会展航洋城') - -console.log('地点咨询回包能够创建可渲染的地点卡片消息') +console.log('地点资料仅由 AI 组织为文本,页面不渲染地点卡片') diff --git a/tests/check-ai-house-markdown.js b/tests/check-ai-house-markdown.js new file mode 100644 index 0000000..ed9afb4 --- /dev/null +++ b/tests/check-ai-house-markdown.js @@ -0,0 +1,24 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') + +const root = path.resolve(__dirname, '..') +const pagePath = path.join(root, 'pages/ai-house/index.vue') +const componentPath = path.join(root, 'uni_modules/mp-html/components/mp-html/mp-html.vue') +const markdownPluginPath = path.join(root, 'uni_modules/mp-html/components/mp-html/markdown/index.js') + +const page = fs.readFileSync(pagePath, 'utf8') +const component = fs.readFileSync(componentPath, 'utf8') + +assert.match(page, /v-if="message\.role === 'ai'"[\s\S]*:content="message\.content"[\s\S]*markdown/) +assert.match(component, /markdown:\s*Boolean/) +assert.match(component, /require\('\.\/markdown\/index\.js'\)/) + +const Markdown = require(markdownPluginPath) +const html = new Markdown({ markdown: true }).onUpdate('# 标题\n\n**重点**\n\n- 房源 A') + +assert.match(html, /]*>标题<\/h1>/) +assert.match(html, /重点<\/strong>/) +assert.match(html, /
  • 房源 A<\/li>/) + +console.log('AI 找房 Markdown 渲染配置检查通过') diff --git a/tests/check-ai-house-quick-questions.js b/tests/check-ai-house-quick-questions.js new file mode 100644 index 0000000..a9f250d --- /dev/null +++ b/tests/check-ai-house-quick-questions.js @@ -0,0 +1,24 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') + +const pagePath = path.resolve(__dirname, '../pages/ai-house/index.vue') +const page = fs.readFileSync(pagePath, 'utf8') + +for (const label of ['面积和价格', '物业和水电', '空调和停车']) { + assert.match(page, new RegExp(`label: '${label}'`)) +} + +for (const question of [ + '介绍一下所有楼盘的面积和价格。', + '介绍一下所有楼盘的物业费和水电收费情况。', + '介绍一下所有楼盘是否配备空调和停车位。' +]) { + assert.ok(page.includes(`question: '${question}'`)) +} + +assert.ok(!page.includes('这套房的面积和价格是多少?')) +assert.ok(!page.includes('这套房的物业费和水电怎么收费?')) +assert.ok(!page.includes('这套房有空调和停车位吗?')) + +console.log('AI 找房全量楼盘快捷提问检查通过') diff --git a/uni_modules/mp-html/components/mp-html/markdown/index.js b/uni_modules/mp-html/components/mp-html/markdown/index.js new file mode 100644 index 0000000..4f5c5a2 --- /dev/null +++ b/uni_modules/mp-html/components/mp-html/markdown/index.js @@ -0,0 +1,34 @@ +/** + * @fileoverview markdown 插件 + * Include marked (https://github.com/markedjs/marked) + * Include github-markdown-css (https://github.com/sindresorhus/github-markdown-css) + */ +const marked = require('./marked.min') +let index = 0 + +function Markdown (vm) { + this.vm = vm + vm._ids = {} +} + +Markdown.prototype.onUpdate = function (content) { + if (this.vm.markdown) { + return marked(content) + } +} + +Markdown.prototype.onParse = function (node, vm) { + if (vm.options.markdown) { + // 中文 id 需要转换,否则无法跳转 + if (vm.options.useAnchor && node.attrs && /[\u4e00-\u9fa5]/.test(node.attrs.id)) { + const id = 't' + index++ + this.vm._ids[node.attrs.id] = id + node.attrs.id = id + } + if (node.name === 'p' || node.name === 'table' || node.name === 'tr' || node.name === 'th' || node.name === 'td' || node.name === 'blockquote' || node.name === 'pre' || node.name === 'code') { + node.attrs.class = `md-${node.name} ${node.attrs.class || ''}` + } + } +} + +module.exports = Markdown diff --git a/uni_modules/mp-html/components/mp-html/markdown/marked.min.js b/uni_modules/mp-html/components/mp-html/markdown/marked.min.js new file mode 100644 index 0000000..96bc7e9 --- /dev/null +++ b/uni_modules/mp-html/components/mp-html/markdown/marked.min.js @@ -0,0 +1,6 @@ +/*! + * marked - a markdown parser + * Copyright (c) 2011-2020, Christopher Jeffrey. (MIT Licensed) + * https://github.com/markedjs/marked + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).marked=t()}(this,function(){"use strict";function i(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n=e.length?{done:!0}:{done:!1,value:e[r++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function n(e){return c[e]}var e,t=(function(t){function e(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1}}t.exports={defaults:e(),getDefaults:e,changeDefaults:function(e){t.exports.defaults=e}}}(e={exports:{}}),e.exports),r=(t.defaults,t.getDefaults,t.changeDefaults,/[&<>"']/),l=/[&<>"']/g,a=/[<>"']|&(?!#?\w+;)/,o=/[<>"']|&(?!#?\w+;)/g,c={"&":"&","<":"<",">":">",'"':""","'":"'"};var u=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function h(e){return e.replace(u,function(e,t){return"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}var g=/(^|[^\[])\^/g;var f=/[^\w:]/g,d=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var k={},b=/^[^:]+:\/*[^/]*$/,m=/^([^:]+:)[\s\S]*$/,x=/^([^:]+:\/*[^/]*)[\s\S]*$/;function w(e,t){k[" "+e]||(b.test(e)?k[" "+e]=e+"/":k[" "+e]=v(e,"/",!0));var n=-1===(e=k[" "+e]).indexOf(":");return"//"===t.substring(0,2)?n?t:e.replace(m,"$1")+t:"/"===t.charAt(0)?n?t:e.replace(x,"$1")+t:e+t}function v(e,t,n){var r=e.length;if(0===r)return"";for(var i=0;it)n.splice(t);else for(;n.length>=1,e+=e;return n+e},q=t.defaults,O=v,C=R,U=_,j=T;function E(e,t,n){var r=t.href,i=t.title?U(t.title):null,t=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:n,href:r,title:i,text:t}:{type:"image",raw:n,href:r,title:i,text:U(t)}}var D=function(){function e(e){this.options=e||q}var t=e.prototype;return t.space=function(e){e=this.rules.block.newline.exec(e);if(e)return 1=n.length?e.slice(n.length):e}).join("\n")}(n,t[3]||"");return{type:"code",raw:n,lang:t[2]&&t[2].trim(),text:e}}},t.heading=function(e){e=this.rules.block.heading.exec(e);if(e)return{type:"heading",raw:e[0],depth:e[1].length,text:e[2]}},t.nptable=function(e){e=this.rules.block.nptable.exec(e);if(e){var t={type:"table",header:C(e[1].replace(/^ *| *\| *$/g,"")),align:e[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:e[3]?e[3].replace(/\n$/,"").split("\n"):[],raw:e[0]};if(t.header.length===t.align.length){for(var n=t.align.length,r=0;r ?/gm,"");return{type:"blockquote",raw:t[0],text:e}}},t.list=function(e){e=this.rules.block.list.exec(e);if(e){for(var t,n,r,i,s,l=e[0],a=e[2],o=1g[0].length||3/i.test(e[0])&&(t=!1),!n&&/^<(pre|code|kbd|script)(\s|>)/i.test(e[0])?n=!0:n&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(e[0])&&(n=!1),{type:this.options.sanitize?"text":"html",raw:e[0],inLink:t,inRawBlock:n,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):U(e[0]):e[0]}},t.link=function(e){var t=this.rules.inline.link.exec(e);if(t){e=j(t[2],"()");-1$/,"$1"))&&e.replace(this.rules.inline._escapes,"$1"),title:r&&r.replace(this.rules.inline._escapes,"$1")},t[0])}},t.reflink=function(e,t){if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){e=(n[2]||n[1]).replace(/\s+/g," ");if((e=t[e.toLowerCase()])&&e.href)return E(n,e,n[0]);var n=n[0].charAt(0);return{type:"text",raw:n,text:n}}},t.strong=function(e,t,n){void 0===n&&(n="");var r=this.rules.inline.strong.start.exec(e);if(r&&(!r[1]||r[1]&&(""===n||this.rules.inline.punctuation.exec(n)))){t=t.slice(-1*e.length);var i,s="**"===r[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(s.lastIndex=0;null!=(r=s.exec(t));)if(i=this.rules.inline.strong.middle.exec(t.slice(0,r.index+3)))return{type:"strong",raw:e.slice(0,i[0].length),text:e.slice(2,i[0].length-2)}}},t.em=function(e,t,n){void 0===n&&(n="");var r=this.rules.inline.em.start.exec(e);if(r&&(!r[1]||r[1]&&(""===n||this.rules.inline.punctuation.exec(n)))){t=t.slice(-1*e.length);var i,s="*"===r[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(s.lastIndex=0;null!=(r=s.exec(t));)if(i=this.rules.inline.em.middle.exec(t.slice(0,r.index+2)))return{type:"em",raw:e.slice(0,i[0].length),text:e.slice(1,i[0].length-1)}}},t.codespan=function(e){var t=this.rules.inline.code.exec(e);if(t){var n=t[2].replace(/\n/g," "),r=/[^ ]/.test(n),e=n.startsWith(" ")&&n.endsWith(" ");return r&&e&&(n=n.substring(1,n.length-1)),n=U(n,!0),{type:"codespan",raw:t[0],text:n}}},t.br=function(e){e=this.rules.inline.br.exec(e);if(e)return{type:"br",raw:e[0]}},t.del=function(e){e=this.rules.inline.del.exec(e);if(e)return{type:"del",raw:e[0],text:e[2]}},t.autolink=function(e,t){e=this.rules.inline.autolink.exec(e);if(e){var n,t="@"===e[2]?"mailto:"+(n=U(this.options.mangle?t(e[1]):e[1])):n=U(e[1]);return{type:"link",raw:e[0],text:n,href:t,tokens:[{type:"text",raw:n,text:n}]}}},t.url=function(e,t){var n,r,i,s;if(n=this.rules.inline.url.exec(e)){if("@"===n[2])i="mailto:"+(r=U(this.options.mangle?t(n[0]):n[0]));else{for(;s=n[0],n[0]=this.rules.inline._backpedal.exec(n[0])[0],s!==n[0];);r=U(n[0]),i="www."===n[1]?"http://"+r:r}return{type:"link",raw:n[0],text:r,href:i,tokens:[{type:"text",raw:r,text:r}]}}},t.inlineText=function(e,t,n){e=this.rules.inline.text.exec(e);if(e){n=t?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(e[0]):U(e[0]):e[0]:U(this.options.smartypants?n(e[0]):e[0]);return{type:"text",raw:e[0],text:n}}},e}(),R=$,T=z,$=A,z={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?! {0,3}bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:R,table:R,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};z.def=T(z.def).replace("label",z._label).replace("title",z._title).getRegex(),z.bullet=/(?:[*+-]|\d{1,9}[.)])/,z.item=/^( *)(bull) ?[^\n]*(?:\n(?! *bull ?)[^\n]*)*/,z.item=T(z.item,"gm").replace(/bull/g,z.bullet).getRegex(),z.listItemStart=T(/^( *)(bull)/).replace("bull",z.bullet).getRegex(),z.list=T(z.list).replace(/bull/g,z.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+z.def.source+")").getRegex(),z._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",z._comment=/|$)/,z.html=T(z.html,"i").replace("comment",z._comment).replace("tag",z._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),z.paragraph=T(z._paragraph).replace("hr",z.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",z._tag).getRegex(),z.blockquote=T(z.blockquote).replace("paragraph",z.paragraph).getRegex(),z.normal=$({},z),z.gfm=$({},z.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n {0,3}([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n {0,3}\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),z.gfm.nptable=T(z.gfm.nptable).replace("hr",z.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",z._tag).getRegex(),z.gfm.table=T(z.gfm.table).replace("hr",z.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|!--)").replace("tag",z._tag).getRegex(),z.pedantic=$({},z.normal,{html:T("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",z._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:R,paragraph:T(z.normal._paragraph).replace("hr",z.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",z.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});R={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:R,tag:"^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation*\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation_\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation*\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:R,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\?@\\[\\]`^{|}~"};R.punctuation=T(R.punctuation).replace(/punctuation/g,R._punctuation).getRegex(),R._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",R._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",R._comment=T(z._comment).replace("(?:--\x3e|$)","--\x3e").getRegex(),R.em.start=T(R.em.start).replace(/punctuation/g,R._punctuation).getRegex(),R.em.middle=T(R.em.middle).replace(/punctuation/g,R._punctuation).replace(/overlapSkip/g,R._overlapSkip).getRegex(),R.em.endAst=T(R.em.endAst,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.em.endUnd=T(R.em.endUnd,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.strong.start=T(R.strong.start).replace(/punctuation/g,R._punctuation).getRegex(),R.strong.middle=T(R.strong.middle).replace(/punctuation/g,R._punctuation).replace(/overlapSkip/g,R._overlapSkip).getRegex(),R.strong.endAst=T(R.strong.endAst,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.strong.endUnd=T(R.strong.endUnd,"g").replace(/punctuation/g,R._punctuation).getRegex(),R.blockSkip=T(R._blockSkip,"g").getRegex(),R.overlapSkip=T(R._overlapSkip,"g").getRegex(),R._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,R._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,R._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,R.autolink=T(R.autolink).replace("scheme",R._scheme).replace("email",R._email).getRegex(),R._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,R.tag=T(R.tag).replace("comment",R._comment).replace("attribute",R._attribute).getRegex(),R._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,R._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,R._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,R.link=T(R.link).replace("label",R._label).replace("href",R._href).replace("title",R._title).getRegex(),R.reflink=T(R.reflink).replace("label",R._label).getRegex(),R.reflinkSearch=T(R.reflinkSearch,"g").replace("reflink",R.reflink).replace("nolink",R.nolink).getRegex(),R.normal=$({},R),R.pedantic=$({},R.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:T(/^!?\[(label)\]\((.*?)\)/).replace("label",R._label).getRegex(),reflink:T(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",R._label).getRegex()}),R.gfm=$({},R.normal,{escape:T(R.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\'+(n?e:V(e,!0))+"\n":"
    "+(n?e:V(e,!0))+"
    \n"},t.blockquote=function(e){return"
    \n"+e+"
    \n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"'+e+"\n":""+e+"\n"},t.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},t.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"\n"},t.listitem=function(e){return"
  • "+e+"
  • \n"},t.checkbox=function(e){return" "},t.paragraph=function(e){return"

    "+e+"

    \n"},t.table=function(e,t){return"\n\n"+e+"\n"+(t=t&&""+t+"")+"
    \n"},t.tablerow=function(e){return"\n"+e+"\n"},t.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+"\n"},t.strong=function(e){return""+e+""},t.em=function(e){return""+e+""},t.codespan=function(e){return""+e+""},t.br=function(){return this.options.xhtml?"
    ":"
    "},t.del=function(e){return""+e+""},t.link=function(e,t,n){if(null===(e=G(this.options.sanitize,this.options.baseUrl,e)))return n;e='"},t.image=function(e,t,n){if(null===(e=G(this.options.sanitize,this.options.baseUrl,e)))return n;n=''+n+'":">"},t.text=function(e){return e},e}(),J=function(){function e(){}var t=e.prototype;return t.strong=function(e){return e},t.em=function(e){return e},t.codespan=function(e){return e},t.del=function(e){return e},t.html=function(e){return e},t.text=function(e){return e},t.link=function(e,t,n){return""+n},t.image=function(e,t,n){return""+n},t.br=function(){return""},e}(),K=function(){function e(){this.seen={}}var t=e.prototype;return t.serialize=function(e){return e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-")},t.getNextSafeSlug=function(e,t){var n=e,r=0;if(this.seen.hasOwnProperty(n))for(r=this.seen[e];n=e+"-"+ ++r,this.seen.hasOwnProperty(n););return t||(this.seen[e]=r,this.seen[n]=0),n},t.slug=function(e,t){void 0===t&&(t={});var n=this.serialize(e);return this.getNextSafeSlug(n,t.dryrun)},e}(),Q=t.defaults,Y=y,ee=function(){function n(e){this.options=e||Q,this.options.renderer=this.options.renderer||new H,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new J,this.slugger=new K}n.parse=function(e,t){return new n(t).parse(e)},n.parseInline=function(e,t){return new n(t).parseInline(e)};var e=n.prototype;return e.parse=function(e,t){void 0===t&&(t=!0);for(var n,r,i,s,l,a,o,c,u,p,h,g,f,d,k,b="",m=e.length,x=0;xAn error occurred:

    "+re(e.message+"",!0)+"
    ";throw e}}return se.options=se.setOptions=function(e){return te(se.defaults,e),ie(se.defaults),se},se.getDefaults=_,se.defaults=t,se.use=function(a){var t,n=te({},a);a.renderer&&function(){var e,l=se.defaults.renderer||new H;for(e in a.renderer)!function(i){var s=l[i];l[i]=function(){for(var e=arguments.length,t=new Array(e),n=0;nAn error occurred:

    "+re(e.message+"",!0)+"
    ";throw e}},se.Parser=ee,se.parser=ee.parse,se.Renderer=H,se.TextRenderer=J,se.Lexer=W,se.lexer=W.lex,se.Tokenizer=D,se.Slugger=K,se.parse=se}); \ No newline at end of file diff --git a/uni_modules/mp-html/components/mp-html/mp-html.vue b/uni_modules/mp-html/components/mp-html/mp-html.vue index f277ad2..03398af 100755 --- a/uni_modules/mp-html/components/mp-html/mp-html.vue +++ b/uni_modules/mp-html/components/mp-html/mp-html.vue @@ -39,7 +39,7 @@ // #ifndef APP-PLUS-NVUE import node from './node/node' // #endif -const plugins=[] +const plugins=[require('./markdown/index.js')] const Parser = require('./parser') // #ifdef APP-PLUS-NVUE const dom = weex.requireModule('dom') @@ -55,6 +55,7 @@ export default { } }, props: { + markdown: Boolean, containerStyle: { type: String, default: '' @@ -152,6 +153,7 @@ export default { * @returns {Promise} */ navigateTo (id, offset) { + id = this._ids[decodeURI(id)] || id return new Promise((resolve, reject) => { if (!this.useAnchor) { reject(Error('Anchor is disabled'))