From 894a9881d75a0d27049a52ca0c84fad5663f3550 Mon Sep 17 00:00:00 2001 From: lik Date: Mon, 8 Jun 2026 12:01:40 +0800 Subject: [PATCH] tmp --- app.js | 20 + app.json | 6 +- .../chat-markdown-code.d.ts | 13 + .../chat-markdown-code/chat-markdown-code.js | 1 + .../chat-markdown-code.json | 1 + .../chat-markdown-code.wxml | 1 + .../chat-markdown-code.wxss | 7 + .../chat-markdown-node.d.ts | 23 + .../chat-markdown-node/chat-markdown-node.js | 1 + .../chat-markdown-node.json | 1 + .../chat-markdown-node.wxml | 1 + .../chat-markdown-node.wxss | 1 + .../chat-markdown-table.d.ts | 13 + .../chat-markdown-table.js | 1 + .../chat-markdown-table.json | 1 + .../chat-markdown-table.wxml | 1 + .../chat-markdown-table.wxss | 12 + .../chat-markdown/chat-markdown.d.ts | 22 + .../chat-markdown/chat-markdown.js | 1 + .../chat-markdown/chat-markdown.json | 1 + .../chat-markdown/chat-markdown.wxml | 1 + .../chat-markdown/chat-markdown.wxss | 28 ++ .../chat-markdown/index.d.ts | 3 + .../chat-markdown/index.js | 1 + .../chat-markdown/props.d.ts | 3 + .../chat-markdown/props.js | 1 + .../chat-markdown/type.d.ts | 17 + .../tdesign-miniprogram/chat-markdown/type.js | 1 + .../miniprogram_npm/marked/index.js | 1 + .../miniprogram_npm/tslib/index.js | 1 + pages/ai/index.js | 267 +++++++++-- pages/ai/index.json | 10 +- pages/ai/index.wxml | 68 ++- pages/ai/index.wxss | 368 +++++++++++++- pages/customer/index.js | 265 +++++++++-- pages/customer/index.json | 12 +- pages/customer/index.less | 329 ++++++++++++- pages/customer/index.wxml | 116 ++++- pages/home/index.js | 140 +++--- pages/home/index.wxml | 69 ++- pages/home/index.wxss | 209 +++++++- pages/order/index.js | 450 +++--------------- pages/order/index.json | 6 +- pages/order/index.less | 230 +++------ pages/set/index.js | 256 ++++++++-- pages/set/index.json | 10 +- pages/set/index.wxml | 93 +++- pages/set/index.wxss | 304 +++++++++++- project.private.config.json | 2 +- utils/api.js | 3 + utils/request.js | 15 +- 51 files changed, 2667 insertions(+), 740 deletions(-) create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.d.ts create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.js create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.json create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxml create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxss create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.d.ts create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.js create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.json create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxml create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxss create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.d.ts create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.js create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.json create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxml create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxss create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.d.ts create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.js create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.json create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxml create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxss create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/index.d.ts create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/index.js create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/props.d.ts create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/props.js create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/type.d.ts create mode 100644 miniprogram_npm/tdesign-miniprogram/chat-markdown/type.js create mode 100644 miniprogram_npm/tdesign-miniprogram/miniprogram_npm/marked/index.js create mode 100644 miniprogram_npm/tdesign-miniprogram/miniprogram_npm/tslib/index.js diff --git a/app.js b/app.js index 4b4347a..9222de5 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,7 @@ // app.js import config from './config'; import createBus from './utils/eventBus'; +const API = require('./utils/api.js'); App({ onLaunch() { @@ -26,6 +27,25 @@ App({ this.connect(); }, + onShow(options) { + wx.login({ + success: (res) => { + if (res.code) { + API.user.wxSignin({ code: res.code }) + .then((data) => { + if (data.code == 0) { + this.globalData.user = data.data.user + this.eventBus.emit('user-login', data.data.user) + } else { + console.log('登录失败!') + } + }) + } else { + } + } + }) + }, + /** 全局事件总线 */ eventBus: createBus(), diff --git a/app.json b/app.json index 1a9b481..2926284 100644 --- a/app.json +++ b/app.json @@ -12,15 +12,15 @@ "subpackages": [], "window": { "backgroundTextStyle": "light", - "navigationBarBackgroundColor": "#1a1f3c", + "navigationBarBackgroundColor": "#ffffff", "navigationBarTitleText": "Weixin", - "navigationBarTextStyle": "white", + "navigationBarTextStyle": "black", "backgroundColor": "#0f1535" }, "tabBar": { "color": "#8a8a8a", "selectedColor": "#1296db", - "backgroundColor": "#0f1535", + "backgroundColor": "#ffffff", "borderStyle": "black", "list": [ { diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.d.ts b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.d.ts new file mode 100644 index 0000000..92dbc6a --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.d.ts @@ -0,0 +1,13 @@ +import { SuperComponent, ComponentsOptionsType } from '../../../../components/common/src/index'; +export default class ChatMarkdownCode extends SuperComponent { + options: ComponentsOptionsType; + properties: { + node: { + type: ObjectConstructor; + value: () => {}; + }; + }; + data: { + classPrefix: string; + }; +} diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.js b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.js new file mode 100644 index 0000000..9a198ac --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.js @@ -0,0 +1 @@ +import{__decorate}from"tslib";import{SuperComponent,wxComponent}from"../../common/src/index";import config from"../../common/config";const{prefix:prefix}=config,name=`${prefix}-chat-markdown-code`;let ChatMarkdownCode=class extends SuperComponent{constructor(){super(...arguments),this.options={multipleSlots:!0},this.properties={node:{type:Object,value:()=>({})}},this.data={classPrefix:name}}};ChatMarkdownCode=__decorate([wxComponent()],ChatMarkdownCode);export default ChatMarkdownCode; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.json b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.json new file mode 100644 index 0000000..c673d75 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.json @@ -0,0 +1 @@ +{"component":true,"styleIsolation":"apply-shared","usingComponents":{}} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxml b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxml new file mode 100644 index 0000000..44407ad --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxml @@ -0,0 +1 @@ +{{node.lang}}{{node.text}} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxss b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxss new file mode 100644 index 0000000..a87bbf8 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-code/chat-markdown-code.wxss @@ -0,0 +1,7 @@ +@import '../../common/style/index.wxss';.t-chat-markdown-code{margin:16rpx 0;border-radius:8rpx;background-color:#f6f8fa;border:1rpx solid #e1e4e8;overflow:hidden;} +.t-chat-markdown-code__header{padding:8rpx 16rpx;background-color:#e1e4e8;border-bottom:1rpx solid #d0d7de;} +.t-chat-markdown-code__lang{font-size:24rpx;color:#656d76;font-weight:500;} +.t-chat-markdown-code__content{padding:16rpx;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;white-space:nowrap;height:auto;} +.t-chat-markdown-code__text{font:var(--td-font-body-medium,28rpx / 44rpx var(--td-font-family,PingFang SC,Microsoft YaHei,Arial Regular));font-family:'SF Mono',Monaco,'Cascadia Code','Roboto Mono',Consolas,'Courier New',monospace;color:#24292f;white-space:pre;word-wrap:normal;word-break:normal;overflow-wrap:normal;display:inline-block;vertical-align:top;min-width:100%;} +.t-chat-markdown-code-light .t-chat-markdown-code-block{background-color:#fff;border-color:#d0d7de;} +.t-chat-markdown-code-light .t-chat-markdown-code-header{background-color:#f6f8fa;border-bottom-color:#d0d7de;} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.d.ts b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.d.ts new file mode 100644 index 0000000..2de77b7 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.d.ts @@ -0,0 +1,23 @@ +import { SuperComponent, ComponentsOptionsType } from '../../../../components/common/src/index'; +export default class ChatMarkdownNode extends SuperComponent { + options: ComponentsOptionsType; + properties: { + nodes: { + type: ArrayConstructor; + value: () => any[]; + }; + }; + data: { + classPrefix: string; + }; + methods: { + linkClick(e: any): void; + getCareMarkdown(): any; + handleClick(event: any, type: any, token: any): void; + }; + lifetimes: { + created(): void; + attached(): void; + detached(): void; + }; +} diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.js b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.js new file mode 100644 index 0000000..cf4a200 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.js @@ -0,0 +1 @@ +import{__decorate}from"tslib";import{SuperComponent,wxComponent}from"../../common/src/index";import config from"../../common/config";const{prefix:prefix}=config,name=`${prefix}-chat-markdown`;let ChatMarkdownNode=class extends SuperComponent{constructor(){super(...arguments),this.options={multipleSlots:!0},this.properties={nodes:{type:Array,value:()=>[]}},this.data={classPrefix:name},this.methods={linkClick(t){var e;const{index:a}=t.currentTarget.dataset||{},r=null===(e=this.data.nodes)||void 0===e?void 0:e[a];this.handleClick(t,"link-tap",r)},getCareMarkdown(){if(this.data.careMarkdown)return this.data.careMarkdown;for(this.setData({careMarkdown:this.selectOwnerComponent()});this.data.careMarkdown.__data__.name!==name;this.setData({careMarkdown:this.data.careMarkdown.selectOwnerComponent()}));return this.data.careMarkdown},handleClick(t,e,a){this.data.getCareMarkdown().triggerEvent("click",{event:t,node:a})}},this.lifetimes={created(){this.data.getCareMarkdown=this.getCareMarkdown.bind(this),this.data.handleClick=this.handleClick.bind(this)},attached(){},detached(){}}}};ChatMarkdownNode=__decorate([wxComponent()],ChatMarkdownNode);export default ChatMarkdownNode; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.json b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.json new file mode 100644 index 0000000..bf7bbc0 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.json @@ -0,0 +1 @@ +{"component":true,"styleIsolation":"apply-shared","usingComponents":{"chat-markdown-table":"../chat-markdown-table/chat-markdown-table","chat-markdown-code":"../chat-markdown-code/chat-markdown-code","chat-markdown-node":"./chat-markdown-node"}} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxml b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxml new file mode 100644 index 0000000..47492e4 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxml @@ -0,0 +1 @@ +{{''+li.text+''}}{{item.title}}{{''+item.raw+''}}{{''+item.text+''}}{{''+item.text+''}}{{''+item.text+''}}{{''+item.text+''}}{{''+(item.text||item.raw)+''}}{{''+(item.text||item.raw)+''}} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxss b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxss new file mode 100644 index 0000000..475b570 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-node/chat-markdown-node.wxss @@ -0,0 +1 @@ +@import '../../common/style/index.wxss'; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.d.ts b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.d.ts new file mode 100644 index 0000000..5ea8a87 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.d.ts @@ -0,0 +1,13 @@ +import { SuperComponent, ComponentsOptionsType } from '../../../../components/common/src/index'; +export default class ChatMarkdownTable extends SuperComponent { + options: ComponentsOptionsType; + properties: { + node: { + type: ObjectConstructor; + value: {}; + }; + }; + data: { + classPrefix: string; + }; +} diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.js b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.js new file mode 100644 index 0000000..cd1fa67 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.js @@ -0,0 +1 @@ +import{__decorate}from"tslib";import{SuperComponent,wxComponent}from"../../common/src/index";import config from"../../common/config";const{prefix:prefix}=config,name=`${prefix}-chat-markdown-table`;let ChatMarkdownTable=class extends SuperComponent{constructor(){super(...arguments),this.options={multipleSlots:!0},this.properties={node:{type:Object,value:{}}},this.data={classPrefix:name}}};ChatMarkdownTable=__decorate([wxComponent()],ChatMarkdownTable);export default ChatMarkdownTable; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.json b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.json new file mode 100644 index 0000000..44ed952 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.json @@ -0,0 +1 @@ +{"component":true,"styleIsolation":"apply-shared","usingComponents":{"chat-markdown-node":"../chat-markdown-node/chat-markdown-node"}} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxml b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxml new file mode 100644 index 0000000..feedcd4 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxss b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxss new file mode 100644 index 0000000..5089c7c --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown-table/chat-markdown-table.wxss @@ -0,0 +1,12 @@ +@import '../../common/style/index.wxss';.t-chat-markdown-table{width:100%;overflow-y:hidden;overflow-x:auto;border:1rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));} +.t-chat-markdown-table__container{display:table;min-width:100%;max-width:max-content;border-collapse:collapse;white-space:nowrap;} +.t-chat-markdown-table__thead{display:table-header-group;} +.t-chat-markdown-table__tbody{display:table-row-group;} +.t-chat-markdown-table__tr{display:table-row;border-bottom:1rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));} +.t-chat-markdown-table__tr:last-child{border-bottom:none;} +.t-chat-markdown-table__tr:nth-child(2n+1){background-color:var(--td-bg-color-container,var(--td-font-white-1,#fff));} +.t-chat-markdown-table__tr:nth-child(2n){background-color:var(--td-bg-color-secondarycontainer,var(--td-gray-color-1,#f3f3f3));} +.t-chat-markdown-table__th{display:table-cell;vertical-align:middle;background-color:var(--td-bg-color-secondarycontainer,var(--td-gray-color-1,#f3f3f3));padding:5rpx 10rpx;color:var(--td-text-color-secondary,var(--td-font-gray-2,rgba(0,0,0,.6)));border-right:1rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));} +.t-chat-markdown-table__th:last-child{border-right:none;} +.t-chat-markdown-table__td{display:table-cell;vertical-align:middle;padding:5rpx 10rpx;border-right:1rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));} +.t-chat-markdown-table__td:last-child{border-right:none;} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.d.ts b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.d.ts new file mode 100644 index 0000000..9422743 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.d.ts @@ -0,0 +1,22 @@ +import { SuperComponent, ComponentsOptionsType } from '../../../components/common/src/index'; +import { TdChatMarkdownProps } from './type'; +export interface ChatMarkdownProps extends TdChatMarkdownProps { +} +export default class ChatMarkdown extends SuperComponent { + options: ComponentsOptionsType; + properties: TdChatMarkdownProps; + data: { + classPrefix: string; + nodes: any[]; + name: string; + }; + observers: { + content: (markdown: string) => void; + }; + methods: { + parseMarkdown(markdown: string): void; + }; + lifetimes: { + attached(): void; + }; +} diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.js b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.js new file mode 100644 index 0000000..0b4c9f8 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.js @@ -0,0 +1 @@ +import{__decorate}from"tslib";import{Lexer}from"marked";import{SuperComponent,wxComponent}from"../common/src/index";import config from"../common/config";import props from"./props";const{prefix:prefix}=config,name=`${prefix}-chat-markdown`;let ChatMarkdown=class extends SuperComponent{constructor(){super(...arguments),this.options={multipleSlots:!0},this.properties=props,this.data={classPrefix:name,nodes:[],name:name},this.observers={content:function(o){this.parseMarkdown(o)}},this.methods={parseMarkdown(o){try{const t=new Lexer(this.data.options).lex(o);this.setData({nodes:t})}catch(t){console.error("Markdown parsing error:",t),this.setData({nodes:[{type:"text",raw:o,text:o}]})}}},this.lifetimes={attached(){}}}};ChatMarkdown=__decorate([wxComponent()],ChatMarkdown);export default ChatMarkdown; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.json b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.json new file mode 100644 index 0000000..b740121 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.json @@ -0,0 +1 @@ +{"component":true,"styleIsolation":"shared","usingComponents":{"chat-markdown-node":"./chat-markdown-node/chat-markdown-node"}} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxml b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxml new file mode 100644 index 0000000..6149adc --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxss b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxss new file mode 100644 index 0000000..de26284 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/chat-markdown.wxss @@ -0,0 +1,28 @@ +@import '../common/style/index.wxss';.t-chat-markdown{color:var(--td-text-color-primary,var(--td-font-gray-1,rgba(0,0,0,.9)));word-wrap:break-word;word-break:break-word;line-height:1.75;} +.t-chat-markdown-inline{display:inline;} +.t-chat-markdown-p{-webkit-margin-before:var(--td-spacer-1,24rpx);margin-block-start:var(--td-spacer-1,24rpx);-webkit-margin-after:var(--td-spacer-1,24rpx);margin-block-end:var(--td-spacer-1,24rpx);} +.t-chat-markdown-p:first-child{-webkit-margin-before:0;margin-block-start:0;} +.t-chat-markdown-p:last-child{-webkit-margin-after:0;margin-block-end:0;} +.t-chat-markdown-blockquote{padding:0 .75em;color:var(--td-text-color-primary,var(--td-font-gray-1,rgba(0,0,0,.9)));background-color:var(--td-bg-color-secondarycontainer,var(--td-gray-color-1,#f3f3f3));border-left:4rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));margin-bottom:var(--td-spacer-1,24rpx);} +.t-chat-markdown-h{font-size:1em;margin:var(--td-spacer-1,24rpx) 0;font-weight:700;} +.t-chat-markdown-h1{font-size:2em;} +.t-chat-markdown-h2{font-size:1.75em;} +.t-chat-markdown-h3{font-size:1.5em;} +.t-chat-markdown-h4{font-size:1.25em;} +.t-chat-markdown-h5{font-size:1em;} +.t-chat-markdown-h6{font-size:.75em;} +.t-chat-markdown-em{font-style:italic;} +.t-chat-markdown-strong{font-weight:700;} +.t-chat-markdown-hr{height:6rpx;padding:0;margin:var(--td-spacer-1,24rpx) 0;background-color:var(--td-component-border,var(--td-gray-color-4,#dcdcdc));border:0;} +.t-chat-markdown-list{display:block;padding:0;margin:0 0 var(--td-spacer,16rpx) 1.5em;} +.t-chat-markdown-list__decimal{list-style-type:decimal;} +.t-chat-markdown-list-item{display:list-item;margin-bottom:var(--td-spacer-1,24rpx);} +.t-chat-markdown-link{color:var(--td-brand-color,var(--td-primary-color-7,#0052d9));} +.t-chat-markdown-del{text-decoration:line-through;} +.t-chat-markdown-codespan{padding:4rpx 8rpx;margin:0 4rpx;border-radius:8rpx;font-size:.8em;overflow-x:auto;background-color:var(--td-bg-color-page,var(--td-gray-color-1,#f3f3f3));border:1rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));} +.t-chat-markdown .t-chat-markdown-table__container{display:table;width:100%;border-collapse:collapse;} +.t-chat-markdown .t-chat-markdown-table__container-thead{display:table-header-group;} +.t-chat-markdown .t-chat-markdown-table__container-tbody{display:table-row-group;} +.t-chat-markdown .t-chat-markdown-table__container-tr{display:table-row;} +.t-chat-markdown .t-chat-markdown-table__container-th{display:table-cell;vertical-align:middle;background-color:var(--td-bg-color-component,var(--td-gray-color-3,#e7e7e7));font-weight:700;padding:5rpx 10rpx;border:1rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));} +.t-chat-markdown .t-chat-markdown-table__container-td{display:table-cell;vertical-align:middle;padding:5rpx 10rpx;border:1rpx solid var(--td-component-border,var(--td-gray-color-4,#dcdcdc));} \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/index.d.ts b/miniprogram_npm/tdesign-miniprogram/chat-markdown/index.d.ts new file mode 100644 index 0000000..3cdf654 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/index.d.ts @@ -0,0 +1,3 @@ +export * from './props'; +export * from './type'; +export * from './chat-markdown'; diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/index.js b/miniprogram_npm/tdesign-miniprogram/chat-markdown/index.js new file mode 100644 index 0000000..24e644f --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/index.js @@ -0,0 +1 @@ +export*from"./props";export*from"./type";export*from"./chat-markdown"; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/props.d.ts b/miniprogram_npm/tdesign-miniprogram/chat-markdown/props.d.ts new file mode 100644 index 0000000..fa9221b --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/props.d.ts @@ -0,0 +1,3 @@ +import { TdChatMarkdownProps } from './type'; +declare const props: TdChatMarkdownProps; +export default props; diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/props.js b/miniprogram_npm/tdesign-miniprogram/chat-markdown/props.js new file mode 100644 index 0000000..6e18e90 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/props.js @@ -0,0 +1 @@ +const props={content:{type:String,value:"",required:!0},options:{type:Object,value:{gfm:!0,pedantic:!1,breaks:!0}}};export default props; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/type.d.ts b/miniprogram_npm/tdesign-miniprogram/chat-markdown/type.d.ts new file mode 100644 index 0000000..5bc39cf --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/type.d.ts @@ -0,0 +1,17 @@ +export interface TdChatMarkdownProps { + content: { + type: StringConstructor; + value?: string; + required?: boolean; + }; + options?: { + type: ObjectConstructor; + value?: TdChatContentMDOptions; + }; +} +export interface TdChatContentMDOptions { + gfm?: boolean; + pedantic?: boolean; + smartLists?: boolean; + breaks?: boolean; +} diff --git a/miniprogram_npm/tdesign-miniprogram/chat-markdown/type.js b/miniprogram_npm/tdesign-miniprogram/chat-markdown/type.js new file mode 100644 index 0000000..17bca46 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/chat-markdown/type.js @@ -0,0 +1 @@ +export{}; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/miniprogram_npm/marked/index.js b/miniprogram_npm/tdesign-miniprogram/miniprogram_npm/marked/index.js new file mode 100644 index 0000000..2107b85 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/miniprogram_npm/marked/index.js @@ -0,0 +1 @@ +function L(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var T=L();function G(e){T=e}var E={exec:()=>null};function d(e,t=""){let n="string"==typeof e?e:e.source,r={replace:(e,t)=>{let s="string"==typeof t?t:t.source;return s=s.replace(m.caret,"$1"),n=n.replace(e,s),r},getRegex:()=>new RegExp(n,t)};return r}var be=(()=>{try{return!!new RegExp("(?<=1)(?/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[\t ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},Re=/^(?:[ \t]*(?:\n|$))+/,Te=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,Oe=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,I=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,we=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,F=/(?:[*+-]|\d{1,9}[.)])/,ie=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,oe=d(ie).replace(/bull/g,F).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),ye=d(ie).replace(/bull/g,F).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),j=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,Pe=/^[^\n]+/,Q=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,Se=d(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",Q).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),$e=d(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,F).getRegex(),v="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|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",U=/|$))/,_e=d("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))","i").replace("comment",U).replace("tag",v).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),ae=d(j).replace("hr",I).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex(),Le=d(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",ae).getRegex(),K={blockquote:Le,code:Te,def:Se,fences:Oe,heading:we,hr:I,html:_e,lheading:oe,list:$e,newline:Re,paragraph:ae,table:E,text:Pe},re=d("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",I).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3}\t)[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex(),Me={...K,lheading:ye,table:re,paragraph:d(j).replace("hr",I).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",re).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",v).getRegex()},ze={...K,html:d("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",U).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+|$)/,fences:E,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:d(j).replace("hr",I).replace("heading"," *#{1,6} *[^\n]").replace("lheading",oe).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Ae=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,Ee=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,le=/^( {2,}|\\)\n(?!\s*$)/,Ie=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\`+)[^`]+\k(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",be?"(?`+)[^`]+\k(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),ce=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,De=d(ce,"u").replace(/punct/g,D).getRegex(),He=d(ce,"u").replace(/punct/g,pe).getRegex(),he="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",Ze=d(he,"gu").replace(/notPunctSpace/g,ue).replace(/punctSpace/g,W).replace(/punct/g,D).getRegex(),Ge=d(he,"gu").replace(/notPunctSpace/g,qe).replace(/punctSpace/g,Be).replace(/punct/g,pe).getRegex(),Ne=d("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,ue).replace(/punctSpace/g,W).replace(/punct/g,D).getRegex(),Fe=d(/\\(punct)/,"gu").replace(/punct/g,D).getRegex(),je=d(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("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])?)+(?![-_])/).getRegex(),Qe=d(U).replace("(?:--\x3e|$)","--\x3e").getRegex(),Ue=d("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",Qe).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),q=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/,Ke=d(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/).replace("label",q).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),de=d(/^!?\[(label)\]\[(ref)\]/).replace("label",q).replace("ref",Q).getRegex(),ke=d(/^!?\[(ref)\](?:\[\])?/).replace("ref",Q).getRegex(),We=d("reflink|nolink(?!\\()","g").replace("reflink",de).replace("nolink",ke).getRegex(),se=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,X={_backpedal:E,anyPunctuation:Fe,autolink:je,blockSkip:ve,br:le,code:Ee,del:E,emStrongLDelim:De,emStrongRDelimAst:Ze,emStrongRDelimUnd:Ne,escape:Ae,link:Ke,nolink:ke,punctuation:Ce,reflink:de,reflinkSearch:We,tag:Ue,text:Ie,url:E},Xe={...X,link:d(/^!?\[(label)\]\((.*?)\)/).replace("label",q).getRegex(),reflink:d(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",q).getRegex()},N={...X,emStrongRDelimAst:Ge,emStrongLDelim:He,url:d(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",se).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:d(/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},ge=e=>Ve[e];function w(e,t){if(t){if(m.escapeTest.test(e))return e.replace(m.escapeReplace,ge)}else if(m.escapeTestNoEncode.test(e))return e.replace(m.escapeReplaceNoEncode,ge);return e}function J(e){try{e=encodeURI(e).replace(m.percentDecode,"%")}catch{return null}return e}function V(e,t){let n=e.replace(m.findPipe,(e,t,n)=>{let r=!1,s=t;for(;--s>=0&&"\\"===n[s];)r=!r;return r?"|":" |"}).split(m.splitPipe),r=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length0?-2:-1}function me(e,t,n,r,s){let l=t.href,i=t.title||null,a=e[1].replace(s.other.outputLinkReplace,"$1");r.state.inLink=!0;let o={type:"!"===e[0].charAt(0)?"image":"link",raw:n,href:l,title:i,text:a,tokens:r.inlineTokens(a)};return r.state.inLink=!1,o}function Ye(e,t,n){let r=e.match(n.other.indentCodeCompensation);if(null===r)return t;let s=r[1];return t.split("\n").map(e=>{let t=e.match(n.other.beginningSpace);if(null===t)return e;let[r]=t;return r.length>=s.length?e.slice(s.length):e}).join("\n")}var y=class{options;rules;lexer;constructor(e){this.options=e||T}space(e){let t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){let t=this.rules.block.code.exec(e);if(t){let e=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:z(e,"\n")}}}fences(e){let t=this.rules.block.fences.exec(e);if(t){let e=t[0],n=Ye(e,t[3]||"",this.rules);return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){let t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(this.rules.other.endingHash.test(e)){let t=z(e,"#");(this.options.pedantic||!t||this.rules.other.endingSpaceChar.test(t))&&(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){let t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:z(t[0],"\n")}}blockquote(e){let t=this.rules.block.blockquote.exec(e);if(t){let e=z(t[0],"\n").split("\n"),n="",r="",s=[];for(;e.length>0;){let t,l=!1,i=[];for(t=0;t1,s={type:"list",raw:"",ordered:r,start:r?+n.slice(0,-1):"",loose:!1,items:[]};n=r?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=r?n:"[*+-]");let l=this.rules.other.listItemRegex(n),i=!1;for(;e;){let n=!1,r="",a="";if(!(t=l.exec(e))||this.rules.block.hr.test(e))break;r=t[0],e=e.substring(r.length);let o=t[2].split("\n",1)[0].replace(this.rules.other.listReplaceTabs,e=>" ".repeat(3*e.length)),c=e.split("\n",1)[0],h=!o.trim(),p=0;if(this.options.pedantic?(p=2,a=o.trimStart()):h?p=t[1].length+1:(p=t[2].search(this.rules.other.nonSpaceChar),p=p>4?1:p,a=o.slice(p),p+=t[1].length),h&&this.rules.other.blankLine.test(c)&&(r+=c+"\n",e=e.substring(c.length+1),n=!0),!n){let t=this.rules.other.nextBulletRegex(p),n=this.rules.other.hrRegex(p),s=this.rules.other.fencesBeginRegex(p),l=this.rules.other.headingBeginRegex(p),i=this.rules.other.htmlBeginRegex(p);for(;e;){let u,g=e.split("\n",1)[0];if(c=g,this.options.pedantic?(c=c.replace(this.rules.other.listReplaceNesting," "),u=c):u=c.replace(this.rules.other.tabCharGlobal," "),s.test(c)||l.test(c)||i.test(c)||t.test(c)||n.test(c))break;if(u.search(this.rules.other.nonSpaceChar)>=p||!c.trim())a+="\n"+u.slice(p);else{if(h||o.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4||s.test(o)||l.test(o)||n.test(o))break;a+="\n"+c}!h&&!c.trim()&&(h=!0),r+=g+"\n",e=e.substring(g.length+1),o=u.slice(p)}}s.loose||(i?s.loose=!0:this.rules.other.doubleBlankLine.test(r)&&(i=!0));let u,g=null;this.options.gfm&&(g=this.rules.other.listIsTask.exec(a),g&&(u="[ ] "!==g[0],a=a.replace(this.rules.other.listReplaceTask,""))),s.items.push({type:"list_item",raw:r,task:!!g,checked:u,loose:!1,text:a,tokens:[]}),s.raw+=r}let a=s.items.at(-1);if(!a)return;a.raw=a.raw.trimEnd(),a.text=a.text.trimEnd(),s.raw=s.raw.trimEnd();for(let e=0;e"space"===e.type),n=t.length>0&&t.some(e=>this.rules.other.anyLine.test(e.raw));s.loose=n}if(s.loose)for(let e=0;e({text:e,tokens:this.lexer.inline(e),header:!1,align:l.align[t]})));return l}}lheading(e){let t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){let t=this.rules.block.paragraph.exec(e);if(t){let e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){let t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){let t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){let t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){let t=this.rules.inline.link.exec(e);if(t){let e=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(e)){if(!this.rules.other.endAngleBracket.test(e))return;let t=z(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{let e=fe(t[2],"()");if(-2===e)return;if(e>-1){let n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],r="";if(this.options.pedantic){let e=this.rules.other.pedanticHrefTitle.exec(n);e&&(n=e[1],r=e[3])}else r=t[3]?t[3].slice(1,-1):"";return n=n.trim(),this.rules.other.startAngleBracket.test(n)&&(n=this.options.pedantic&&!this.rules.other.endAngleBracket.test(e)?n.slice(1):n.slice(1,-1)),me(t,{href:n&&n.replace(this.rules.inline.anyPunctuation,"$1"),title:r&&r.replace(this.rules.inline.anyPunctuation,"$1")},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){let e=t[(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," ").toLowerCase()];if(!e){let e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return me(n,e,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let r=this.rules.inline.emStrongLDelim.exec(e);if(!(!r||r[3]&&n.match(this.rules.other.unicodeAlphaNumeric))&&(!r[1]&&!r[2]||!n||this.rules.inline.punctuation.exec(n))){let n,s,l=[...r[0]].length-1,i=l,a=0,o="*"===r[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(o.lastIndex=0,t=t.slice(-1*e.length+l);null!=(r=o.exec(t));){if(n=r[1]||r[2]||r[3]||r[4]||r[5]||r[6],!n)continue;if(s=[...n].length,r[3]||r[4]){i+=s;continue}if((r[5]||r[6])&&l%3&&!((l+s)%3)){a+=s;continue}if(i-=s,i>0)continue;s=Math.min(s,s+i+a);let t=[...r[0]][0].length,o=e.slice(0,l+r.index+t+s);if(Math.min(l,s)%2){let e=o.slice(1,-1);return{type:"em",raw:o,text:e,tokens:this.lexer.inlineTokens(e)}}let c=o.slice(2,-2);return{type:"strong",raw:o,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){let t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(this.rules.other.newLineCharGlobal," "),n=this.rules.other.nonSpaceChar.test(e),r=this.rules.other.startingSpaceChar.test(e)&&this.rules.other.endingSpaceChar.test(e);return n&&r&&(e=e.substring(1,e.length-1)),{type:"codespan",raw:t[0],text:e}}}br(e){let t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){let t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){let t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=t[1],n="mailto:"+e):(e=t[1],n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=t[0],n="mailto:"+e;else{let r;do{r=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(r!==t[0]);e=t[0],n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){let t=this.rules.inline.text.exec(e);if(t){let e=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:e}}}},x=class e{tokens;options;state;tokenizer;inlineQueue;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||T,this.options.tokenizer=this.options.tokenizer||new y,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:m,block:C.normal,inline:M.normal};this.options.pedantic?(t.block=C.pedantic,t.inline=M.pedantic):this.options.gfm&&(t.block=C.gfm,this.options.breaks?t.inline=M.breaks:t.inline=M.gfm),this.tokenizer.rules=t}static get rules(){return{block:C,inline:M}}static lex(t,n){return new e(n).lex(t)}static lexInline(t,n){return new e(n).inlineTokens(t)}lex(e){e=e.replace(m.carriageReturn,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e!!(r=n.call({lexer:this},e,t))&&(e=e.substring(r.raw.length),t.push(r),!0)))continue;if(r=this.tokenizer.space(e)){e=e.substring(r.raw.length);let n=t.at(-1);1===r.raw.length&&void 0!==n?n.raw+="\n":t.push(r);continue}if(r=this.tokenizer.code(e)){e=e.substring(r.raw.length);let n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+=(n.raw.endsWith("\n")?"":"\n")+r.raw,n.text+="\n"+r.text,this.inlineQueue.at(-1).src=n.text):t.push(r);continue}if(r=this.tokenizer.fences(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.heading(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.hr(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.blockquote(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.list(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.html(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.def(e)){e=e.substring(r.raw.length);let n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+=(n.raw.endsWith("\n")?"":"\n")+r.raw,n.text+="\n"+r.raw,this.inlineQueue.at(-1).src=n.text):this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title},t.push(r));continue}if(r=this.tokenizer.table(e)){e=e.substring(r.raw.length),t.push(r);continue}if(r=this.tokenizer.lheading(e)){e=e.substring(r.raw.length),t.push(r);continue}let s=e;if(this.options.extensions?.startBlock){let t,n=1/0,r=e.slice(1);this.options.extensions.startBlock.forEach(e=>{t=e.call({lexer:this},r),"number"==typeof t&&t>=0&&(n=Math.min(n,t))}),n<1/0&&n>=0&&(s=e.substring(0,n+1))}if(this.state.top&&(r=this.tokenizer.paragraph(s))){let l=t.at(-1);n&&"paragraph"===l?.type?(l.raw+=(l.raw.endsWith("\n")?"":"\n")+r.raw,l.text+="\n"+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=l.text):t.push(r),n=s.length!==e.length,e=e.substring(r.raw.length);continue}if(r=this.tokenizer.text(e)){e=e.substring(r.raw.length);let n=t.at(-1);"text"===n?.type?(n.raw+=(n.raw.endsWith("\n")?"":"\n")+r.raw,n.text+="\n"+r.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=n.text):t.push(r);continue}if(e){let t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n,r=e,s=null;if(this.tokens.links){let e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(s=this.tokenizer.rules.inline.reflinkSearch.exec(r));)e.includes(s[0].slice(s[0].lastIndexOf("[")+1,-1))&&(r=r.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+r.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(s=this.tokenizer.rules.inline.anyPunctuation.exec(r));)r=r.slice(0,s.index)+"++"+r.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;null!=(s=this.tokenizer.rules.inline.blockSkip.exec(r));)n=s[2]?s[2].length:0,r=r.slice(0,s.index+n)+"["+"a".repeat(s[0].length-n-2)+"]"+r.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);r=this.options.hooks?.emStrongMask?.call({lexer:this},r)??r;let l=!1,i="";for(;e;){let n;if(l||(i=""),l=!1,this.options.extensions?.inline?.some(r=>!!(n=r.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))continue;if(n=this.tokenizer.escape(e)){e=e.substring(n.raw.length),t.push(n);continue}if(n=this.tokenizer.tag(e)){e=e.substring(n.raw.length),t.push(n);continue}if(n=this.tokenizer.link(e)){e=e.substring(n.raw.length),t.push(n);continue}if(n=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(n.raw.length);let r=t.at(-1);"text"===n.type&&"text"===r?.type?(r.raw+=n.raw,r.text+=n.text):t.push(n);continue}if(n=this.tokenizer.emStrong(e,r,i)){e=e.substring(n.raw.length),t.push(n);continue}if(n=this.tokenizer.codespan(e)){e=e.substring(n.raw.length),t.push(n);continue}if(n=this.tokenizer.br(e)){e=e.substring(n.raw.length),t.push(n);continue}if(n=this.tokenizer.del(e)){e=e.substring(n.raw.length),t.push(n);continue}if(n=this.tokenizer.autolink(e)){e=e.substring(n.raw.length),t.push(n);continue}if(!this.state.inLink&&(n=this.tokenizer.url(e))){e=e.substring(n.raw.length),t.push(n);continue}let s=e;if(this.options.extensions?.startInline){let t,n=1/0,r=e.slice(1);this.options.extensions.startInline.forEach(e=>{t=e.call({lexer:this},r),"number"==typeof t&&t>=0&&(n=Math.min(n,t))}),n<1/0&&n>=0&&(s=e.substring(0,n+1))}if(n=this.tokenizer.inlineText(s)){e=e.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(i=n.raw.slice(-1)),l=!0;let r=t.at(-1);"text"===r?.type?(r.raw+=n.raw,r.text+=n.text):t.push(n);continue}if(e){let t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return t}},P=class{options;parser;constructor(e){this.options=e||T}space(e){return""}code({text:e,lang:t,escaped:n}){let r=(t||"").match(m.notSpaceStart)?.[0],s=e.replace(m.endingNewline,"")+"\n";return r?'
'+(n?s:w(s,!0))+"
\n":"
"+(n?s:w(s,!0))+"
\n"}blockquote({tokens:e}){return`
\n${this.parser.parse(e)}
\n`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return"
\n"}list(e){let t=e.ordered,n=e.start,r="";for(let t=0;t\n"+r+"\n"}listitem(e){let t="";if(e.task){let n=this.checkbox({checked:!!e.checked});e.loose?"paragraph"===e.tokens[0]?.type?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&"text"===e.tokens[0].tokens[0].type&&(e.tokens[0].tokens[0].text=n+" "+w(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`
  • ${t}
  • \n`}checkbox({checked:e}){return"'}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t="",n="";for(let t=0;t${r}`),"\n\n"+t+"\n"+r+"
    \n"}tablerow({text:e}){return`\n${e}\n`}tablecell(e){let t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${w(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){let r=this.parser.parseInline(n),s=J(e);if(null===s)return r;let l='
    ",l}image({href:e,title:t,text:n,tokens:r}){r&&(n=this.parser.parseInline(r,this.parser.textRenderer));let s=J(e);if(null===s)return w(n);let l=`${n}{let s=e[r].flat(1/0);n=n.concat(this.walkTokens(s,t))}):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){let t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach(e=>{let n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach(e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){let n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let r=e.renderer.apply(this,t);return!1===r&&(r=n.apply(this,t)),r}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");let n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)}),n.extensions=t),e.renderer){let t=this.defaults.renderer||new P(this.defaults);for(let n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;let r=n,s=e.renderer[r],l=t[r];t[r]=(...e)=>{let n=s.apply(t,e);return!1===n&&(n=l.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){let t=this.defaults.tokenizer||new y(this.defaults);for(let n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;let r=n,s=e.tokenizer[r],l=t[r];t[r]=(...e)=>{let n=s.apply(t,e);return!1===n&&(n=l.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){let t=this.defaults.hooks||new S;for(let n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;let r=n,s=e.hooks[r],l=t[r];S.passThroughHooks.has(n)?t[r]=e=>{if(this.defaults.async&&S.passThroughHooksRespectAsync.has(n))return(async()=>{let n=await s.call(t,e);return l.call(t,n)})();let r=s.call(t,e);return l.call(t,r)}:t[r]=(...e)=>{if(this.defaults.async)return(async()=>{let n=await s.apply(t,e);return!1===n&&(n=await l.apply(t,e)),n})();let n=s.apply(t,e);return!1===n&&(n=l.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){let t=this.defaults.walkTokens,r=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(r.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}}),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return x.lex(e,t??this.defaults)}parser(e,t){return b.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{let r={...n},s={...this.defaults,...r},l=this.onError(!!s.silent,!!s.async);if(!0===this.defaults.async&&!1===r.async)return l(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof t>"u"||null===t)return l(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return l(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));if(s.hooks&&(s.hooks.options=s,s.hooks.block=e),s.async)return(async()=>{let n=s.hooks?await s.hooks.preprocess(t):t,r=await(s.hooks?await s.hooks.provideLexer():e?x.lex:x.lexInline)(n,s),l=s.hooks?await s.hooks.processAllTokens(r):r;s.walkTokens&&await Promise.all(this.walkTokens(l,s.walkTokens));let i=await(s.hooks?await s.hooks.provideParser():e?b.parse:b.parseInline)(l,s);return s.hooks?await s.hooks.postprocess(i):i})().catch(l);try{s.hooks&&(t=s.hooks.preprocess(t));let n=(s.hooks?s.hooks.provideLexer():e?x.lex:x.lexInline)(t,s);s.hooks&&(n=s.hooks.processAllTokens(n)),s.walkTokens&&this.walkTokens(n,s.walkTokens);let r=(s.hooks?s.hooks.provideParser():e?b.parse:b.parseInline)(n,s);return s.hooks&&(r=s.hooks.postprocess(r)),r}catch(e){return l(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){let e="

    An error occurred:

    "+w(n.message+"",!0)+"
    ";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}},_=new B;function k(e,t){return _.parse(e,t)}k.options=k.setOptions=function(e){return _.setOptions(e),k.defaults=_.defaults,G(k.defaults),k},k.getDefaults=L,k.defaults=T,k.use=function(...e){return _.use(...e),k.defaults=_.defaults,G(k.defaults),k},k.walkTokens=function(e,t){return _.walkTokens(e,t)},k.parseInline=_.parseInline,k.Parser=b,k.parser=b.parse,k.Renderer=P,k.TextRenderer=$,k.Lexer=x,k.lexer=x.lex,k.Tokenizer=y,k.Hooks=S,k.parse=k;var Zt=k.options,Gt=k.setOptions,Nt=k.use,Ft=k.walkTokens,jt=k.parseInline,Qt=k,Ut=b.parse,Kt=x.lex;export{S as Hooks,x as Lexer,B as Marked,b as Parser,P as Renderer,$ as TextRenderer,y as Tokenizer,T as defaults,L as getDefaults,Kt as lexer,k as marked,Zt as options,Qt as parse,jt as parseInline,Ut as parser,Gt as setOptions,Nt as use,Ft as walkTokens}; \ No newline at end of file diff --git a/miniprogram_npm/tdesign-miniprogram/miniprogram_npm/tslib/index.js b/miniprogram_npm/tdesign-miniprogram/miniprogram_npm/tslib/index.js new file mode 100644 index 0000000..ba9c5d8 --- /dev/null +++ b/miniprogram_npm/tdesign-miniprogram/miniprogram_npm/tslib/index.js @@ -0,0 +1 @@ +var __extends,__assign,__rest,__decorate,__param,__esDecorate,__runInitializers,__propKey,__setFunctionName,__metadata,__awaiter,__generator,__exportStar,__values,__read,__spread,__spreadArrays,__spreadArray,__await,__asyncGenerator,__asyncDelegator,__asyncValues,__makeTemplateObject,__importStar,__importDefault,__classPrivateFieldGet,__classPrivateFieldSet,__classPrivateFieldIn,__createBinding,__addDisposableResource,__disposeResources,__rewriteRelativeImportExtension;!function(e){var t="object"==typeof global?global:"object"==typeof self?self:"object"==typeof this?this:{};function r(e,r){return e!==t&&("function"==typeof Object.create?Object.defineProperty(e,"__esModule",{value:!0}):e.__esModule=!0),function(t,n){return e[t]=r?r(t,n):n}}"function"==typeof define&&define.amd?define("tslib",["exports"],function(n){e(r(t,r(n)))}):"object"==typeof module&&"object"==typeof module.exports?e(r(t,r(module.exports))):e(r(t))}(function(e){var t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])};__extends=function(e,r){if("function"!=typeof r&&null!==r)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");function n(){this.constructor=e}t(e,r),e.prototype=null===r?Object.create(r):(n.prototype=r.prototype,new n)},__assign=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=0;c--)(o=e[c])&&(i=(a<3?o(i):a>3?o(t,r,i):o(t,r))||i);return a>3&&i&&Object.defineProperty(t,r,i),i},__param=function(e,t){return function(r,n){t(r,n,e)}},__esDecorate=function(e,t,r,n,o,a){function i(e){if(void 0!==e&&"function"!=typeof e)throw new TypeError("Function expected");return e}for(var c,s=n.kind,_="getter"===s?"get":"setter"===s?"set":"value",u=!t&&e?n.static?e:e.prototype:null,l=t||(u?Object.getOwnPropertyDescriptor(u,n.name):{}),f=!1,p=r.length-1;p>=0;p--){var y={};for(var d in n)y[d]="access"===d?{}:n[d];for(var d in n.access)y.access[d]=n.access[d];y.addInitializer=function(e){if(f)throw new TypeError("Cannot add initializers after decoration has completed");a.push(i(e||null))};var v=(0,r[p])("accessor"===s?{get:l.get,set:l.set}:l[_],y);if("accessor"===s){if(void 0===v)continue;if(null===v||"object"!=typeof v)throw new TypeError("Object expected");(c=i(v.get))&&(l.get=c),(c=i(v.set))&&(l.set=c),(c=i(v.init))&&o.unshift(c)}else(c=i(v))&&("field"===s?o.unshift(c):l[_]=c)}u&&Object.defineProperty(u,n.name,l),f=!0},__runInitializers=function(e,t,r){for(var n=arguments.length>2,o=0;o0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},__read=function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},__spread=function(){for(var e=[],t=0;t1||c(e,t)})},t&&(n[e]=t(n[e])))}function c(e,t){try{(r=o[e](t)).value instanceof __await?Promise.resolve(r.value.v).then(s,_):u(a[0][2],r)}catch(e){u(a[0][3],e)}var r}function s(e){c("next",e)}function _(e){c("throw",e)}function u(e,t){e(t),a.shift(),a.length&&c(a[0][0],a[0][1])}},__asyncDelegator=function(e){var t,r;return t={},n("next"),n("throw",function(e){throw e}),n("return"),t[Symbol.iterator]=function(){return this},t;function n(n,o){t[n]=e[n]?function(t){return(r=!r)?{value:__await(e[n](t)),done:!1}:o?o(t):t}:o}},__asyncValues=function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,r=e[Symbol.asyncIterator];return r?r.call(e):(e="function"==typeof __values?__values(e):e[Symbol.iterator](),t={},n("next"),n("throw"),n("return"),t[Symbol.asyncIterator]=function(){return this},t);function n(r){t[r]=e[r]&&function(t){return new Promise(function(n,o){(function(e,t,r,n){Promise.resolve(n).then(function(t){e({value:t,done:r})},t)})(n,o,(t=e[r](t)).done,t.value)})}}},__makeTemplateObject=function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e};var r=Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t},n=function(e){return n=Object.getOwnPropertyNames||function(e){var t=[];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r);return t},n(e)};__importStar=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var o=n(e),a=0;a { + console.log('[AIChat] Socket opened') + }) + + this.socket.onMessage((data) => { + if (data && data.type === 'ai') { + this.handleAIReply(data) + } else if (data && data.type === 'system') { + console.log('[AIChat] System:', data.content) + } + }) + + this.socket.onError((err) => { + console.error('[AIChat] WebSocket error', err) + this.handleAIError('网络连接失败,请检查网络设置') + }) + + this.socket.onClose((res) => { + console.log('[AIChat] Socket closed', res) + }) + + this.socket.connect().catch((err) => { + console.error('[AIChat] Connect failed', err) + }) }, - /** - * 页面上拉触底事件的处理函数 - */ - onReachBottom() { + handleAIReply(data) { + let messages = null + if (this.data.messages.length > 0) { + let lastMessage = this.data.messages[this.data.messages.length - 1] + if (lastMessage.type === 'ai' && lastMessage._serverId === data.id) { + lastMessage.content += data.content || '' + messages = this.data.messages + } + } + if (!messages) { + const aiResponse = { + id: `msg_${++this.messageIdCounter}`, + _serverId: data.id, + type: 'ai', + contentType: 'text', + content: data.content || '', + time: this.formatTime(new Date()) + } + + if (data.sessionId) { + wx.setStorageSync('admin_ai_session_id', data.sessionId) + } + + messages = [...this.data.messages, aiResponse] + } + + this.setData({ + messages: messages, + isTyping: false + }) + this.saveChatHistory(messages) + this.scrollToBottom() }, - /** - * 用户点击右上角分享 - */ - onShareAppMessage() { + loadChatHistory() { + try { + const history = wx.getStorageSync('admin_ai_chat_history') + if (history && history.length > 0) { + this.setData({ messages: history }) + this.scrollToBottom() + } + } catch (e) { + console.log('加载聊天记录失败', e) + } + }, + saveChatHistory(messages) { + try { + wx.setStorageSync('admin_ai_chat_history', messages) + } catch (e) { + console.log('保存聊天记录失败', e) + } + }, + + onInputChange(e) { + const value = e.detail.value + this.setData({ + inputValue: value, + canSend: !!value.trim() + }) + }, + + onQuickQuestionTap(e) { + const question = e.currentTarget.dataset.question + this.setData({ inputValue: question, canSend: true }) + this.sendMessage() + }, + + sendMessage() { + const content = this.data.inputValue.trim() + if (!content) return + + const userMessage = { + id: `msg_${++this.messageIdCounter}`, + type: 'user', + contentType: 'text', + content: content, + time: this.formatTime(new Date()) + } + + const messages = [...this.data.messages, userMessage] + this.setData({ + messages: messages, + inputValue: '', + canSend: false, + isTyping: true + }) + + this.saveChatHistory(messages) + this.scrollToBottom() + + this.sendToAI(content) + }, + + sendToAI(content, type = 'chat') { + const app = getApp() + const user = app.globalData.user + + if (!this.socket) { + this.handleAIError('网络连接失败,请检查网络设置') + return + } + + if (!this.socket.isConnected) { + this.socket.connect().then(() => { + this._doSend(type, content, user, app) + }).catch((err) => { + console.error('[AIChat] Reconnect failed', err) + this.handleAIError('网络连接失败,请检查网络设置') + }) + } else { + this._doSend(type, content, user, app) + } + }, + + _doSend(type, content, user, app) { + this.socket.send({ + type: type, + content: content, + userId: user ? user._id : '', + token: user ? user.security.token : '', + appId: app.globalData.appId || '', + agent: 'escort-admin' + }) + }, + + handleAIError(errorMessage) { + const errorResponse = { + id: `msg_${++this.messageIdCounter}`, + type: 'ai', + contentType: 'text', + content: errorMessage, + time: this.formatTime(new Date()) + } + + const messages = [...this.data.messages, errorResponse] + this.setData({ + messages: messages, + isTyping: false + }) + this.saveChatHistory(messages) + this.scrollToBottom() + }, + + scrollToBottom() { + setTimeout(() => { + const lastMessage = this.data.messages[this.data.messages.length - 1] + if (lastMessage) { + this.setData({ + scrollToMessage: `msg-${lastMessage.id}` + }) + } + }, 100) + }, + + formatTime(date) { + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + return `${hours}:${minutes}` + }, + + clearChat() { + wx.showModal({ + title: '确认清空', + content: '确定要清空所有聊天记录吗?', + success: (res) => { + if (res.confirm) { + this.sendToAI('clear', 'clear') + this.setData({ messages: [] }) + wx.removeStorageSync('admin_ai_chat_history') + wx.removeStorageSync('admin_ai_session_id') + } + } + }) } -}) \ No newline at end of file +}) diff --git a/pages/ai/index.json b/pages/ai/index.json index 8835af0..a185282 100644 --- a/pages/ai/index.json +++ b/pages/ai/index.json @@ -1,3 +1,9 @@ { - "usingComponents": {} -} \ No newline at end of file + "navigationBarTitleText": "AI助手", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#ffffff", + "usingComponents": { + "t-chat-markdown": "tdesign-miniprogram/chat-markdown/chat-markdown" + } +} diff --git a/pages/ai/index.wxml b/pages/ai/index.wxml index c5db65d..428c302 100644 --- a/pages/ai/index.wxml +++ b/pages/ai/index.wxml @@ -1,2 +1,66 @@ - -pages/ai/index.wxml \ No newline at end of file + + + + 您好,我是您的AI助手,有什么可以帮您? + + + {{item}} + + + + + + + {{item.type === 'user' ? '我' : 'AI'}} + + + + + {{item.content}} + + {{item.time}} + + + + + + AI + + + + + + + + + + + + + 清空记录 + + + + + + + 发送 + + + + diff --git a/pages/ai/index.wxss b/pages/ai/index.wxss index 4b1fd54..cb170d5 100644 --- a/pages/ai/index.wxss +++ b/pages/ai/index.wxss @@ -1 +1,367 @@ -/* pages/ai/index.wxss */ \ No newline at end of file +page { + background-color: #f5f6fa; + color: #1a1a2e; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +.chat-container { + display: flex; + flex-direction: column; + height: 100vh; + background-color: #f5f6fa; +} + +.chat-header { + display: flex; + align-items: center; + justify-content: space-between; + height: 88rpx; + padding: 0 24rpx; + background-color: #ffffff; + border-bottom: 1rpx solid #e5e7eb; + flex-shrink: 0; +} + +.chat-header-title { + font-size: 30rpx; + font-weight: 600; + color: #1a1a2e; +} + +.message-list { + flex: 1; + padding: 20rpx; + padding-bottom: 200rpx; + box-sizing: border-box; + overflow: hidden; +} + +.quick-questions { + display: flex; + flex-direction: column; + align-items: center; + padding: 40rpx 20rpx; +} + +.welcome-text { + font-size: 32rpx; + color: #1a1a2e; + margin-bottom: 40rpx; + text-align: center; + font-weight: 500; +} + +.quick-list { + display: flex; + flex-direction: column; + width: 100%; + gap: 20rpx; +} + +.quick-item { + background-color: #ffffff; + border-radius: 16rpx; + padding: 24rpx 32rpx; + font-size: 28rpx; + color: #4c6ef5; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); + text-align: center; + border: 1rpx solid #e5e7eb; + transition: all 0.2s ease; +} + +.quick-item:active { + transform: scale(0.98); + background-color: rgba(76, 110, 245, 0.05); +} + +.message-item { + display: flex; + flex-direction: column; + margin-bottom: 32rpx; +} + +.message-item.ai { + align-items: flex-start; +} + +.message-item.user { + align-items: flex-end; +} + +.message-time { + font-size: 22rpx; + color: #9ca3af; + margin-top: 8rpx; + padding: 0 4rpx; +} + +.message-item.ai .message-time { + text-align: left; + align-self: flex-start; +} + +.message-item.user .message-time { + text-align: right; + align-self: flex-end; +} + +.avatar { + width: 64rpx; + height: 64rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + margin-bottom: 12rpx; +} + +.user-avatar { + background: linear-gradient(135deg, #4c6ef5, #748ffc); + box-shadow: 0 4rpx 12rpx rgba(76, 110, 245, 0.25); +} + +.ai-avatar { + background: linear-gradient(135deg, #20c997, #51cf66); + box-shadow: 0 4rpx 12rpx rgba(32, 201, 151, 0.25); +} + +.avatar-text { + color: #fff; + font-size: 26rpx; + font-weight: 500; +} + +.message-content { + display: flex; + flex-direction: column; + max-width: 95%; +} + +.message-item.user .message-content { + align-items: flex-end; +} + +.message-item.ai .message-content { + align-items: flex-start; +} + +.message-bubble { + padding: 20rpx 24rpx; + border-radius: 16rpx; + font-size: 28rpx; + line-height: 1.6; + word-break: break-word; + max-width: 100%; +} + +.user-bubble { + background: linear-gradient(135deg, #4c6ef5, #748ffc); + color: #fff; + border-bottom-right-radius: 4rpx; + box-shadow: 0 4rpx 12rpx rgba(76, 110, 245, 0.2); + margin-right: 4rpx; +} + +.ai-bubble { + background-color: #ffffff; + color: #1a1a2e; + border-bottom-left-radius: 4rpx; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid #e5e7eb; + margin-left: 4rpx; +} + +/* Markdown 渲染区域 */ +.message-markdown { + width: 100%; + font-size: 28rpx; + color: #1a1a2e; + line-height: 1.6; +} + +/* 表格样式覆盖(防止过宽) */ +.message-markdown .t-chat-markdown-table { + width: 100%; + max-width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + border: 1rpx solid #e5e7eb; + border-radius: 8rpx; +} + +.message-markdown .t-chat-markdown-table__container { + display: table; + min-width: 100%; + max-width: max-content; + border-collapse: collapse; + white-space: normal; + table-layout: fixed; + word-break: break-word; +} + +.message-markdown .t-chat-markdown-table__th, +.message-markdown .t-chat-markdown-table__td { + padding: 8rpx 12rpx; + font-size: 24rpx; + line-height: 1.4; + white-space: normal; + word-break: break-word; + word-wrap: break-word; + overflow-wrap: break-word; + max-width: 320rpx; +} + +.message-markdown .t-chat-markdown-table__th { + background-color: #f5f6fa; + font-weight: 600; + color: #1a1a2e; +} + +.message-markdown .t-chat-markdown-table__tr:nth-child(2n) { + background-color: #fafbfc; +} + +/* 代码块样式覆盖 */ +.message-markdown .t-chat-markdown-codespan, +.message-markdown .t-chat-markdown-code { + font-size: 24rpx; + word-break: break-all; +} + +.typing-indicator { + display: flex; + flex-direction: column; + align-items: flex-start; + margin-bottom: 32rpx; +} + +.typing-bubble { + display: flex; + align-items: center; + gap: 12rpx; + background-color: #ffffff; + padding: 24rpx 32rpx; + border-radius: 16rpx; + border-bottom-left-radius: 4rpx; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); + border: 1rpx solid #e5e7eb; +} + +.dot { + width: 16rpx; + height: 16rpx; + background-color: #9ca3af; + border-radius: 50%; + animation: bounce 1.4s infinite ease-in-out both; +} + +.dot:nth-child(1) { + animation-delay: -0.32s; +} + +.dot:nth-child(2) { + animation-delay: -0.16s; +} + +@keyframes bounce { + 0%, 80%, 100% { + transform: scale(0.6); + } + 40% { + transform: scale(1); + } +} + +.input-area { + display: flex; + align-items: center; + padding: 20rpx 24rpx; + background-color: #ffffff; + gap: 16rpx; + box-sizing: border-box; +} + +.chat-footer { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 10; + background-color: #ffffff; +} + +.chat-toolbar { + display: flex; + align-items: right; + justify-content: right; + padding: 12rpx 24rpx; + background-color: #f5f6fa; + border-top: 1rpx solid #e5e7eb; + border-bottom: 1rpx solid #e5e7eb; +} + +.chat-toolbar-action { + display: flex; + align-items: right; + justify-content: right; + padding: 6rpx 24rpx; + border-radius: 24rpx; + background-color: #ffffff; + border: 1rpx solid #e5e7eb; + transition: all 0.2s ease; +} + +.chat-toolbar-action:active { + transform: scale(0.96); + background-color: rgba(76, 110, 245, 0.08); + border-color: #4c6ef5; +} + +.chat-toolbar-action text { + font-size: 24rpx; + color: #6b7280; +} + +.chat-input { + flex: 1; + height: 72rpx; + background-color: #f5f6fa; + border-radius: 36rpx; + padding: 0 28rpx; + font-size: 28rpx; + color: #1a1a2e; + border: 1rpx solid #e5e7eb; +} + +.chat-input::placeholder { + color: #9ca3af; +} + +.send-btn { + width: 120rpx; + height: 72rpx; + background-color: #e5e7eb; + border-radius: 36rpx; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; +} + +.send-btn.active { + background: linear-gradient(135deg, #4c6ef5, #748ffc); + box-shadow: 0 4rpx 12rpx rgba(76, 110, 245, 0.25); +} + +.send-text { + color: #9ca3af; + font-size: 28rpx; + font-weight: 500; + transition: color 0.2s ease; +} + +.send-btn.active .send-text { + color: #FFFFFF; +} diff --git a/pages/customer/index.js b/pages/customer/index.js index 99bb627..a4716e0 100644 --- a/pages/customer/index.js +++ b/pages/customer/index.js @@ -1,66 +1,269 @@ // pages/customer/index.js +const API = require('../../utils/api.js') + +// 性别映射 +const SEX_MAP = { + male: { label: '男', text: '先生' }, + female: { label: '女', text: '女士' }, + other: { label: '其他', text: '' }, + '': { label: '未知', text: '' } +} + Page({ - - /** - * 页面的初始数据 - */ data: { - + // 搜索相关 + searchKey: '', + // 客户列表 + customerList: [], + // 分页 + page: 1, + pageSize: 10, + hasMore: true, + // 加载状态 + isLoading: false, + isLoadingMore: false, + isRefreshing: false, + // 统计数据 + stats: { + total: 0, + male: 0, + female: 0 + } }, - /** - * 生命周期函数--监听页面加载 - */ onLoad(options) { - + this.loadCustomerList(); }, - /** - * 生命周期函数--监听页面初次渲染完成 - */ - onReady() { - - }, - - /** - * 生命周期函数--监听页面显示 - */ onShow() { - + // 页面显示时如果已有数据则刷新 + if (this.data.customerList.length > 0) { + this.refreshData(); + } }, /** - * 生命周期函数--监听页面隐藏 + * 格式化日期 */ - onHide() { - + formatDate(dateStr) { + if (!dateStr) return ''; + const date = new Date(dateStr); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; }, /** - * 生命周期函数--监听页面卸载 + * 处理客户数据 */ - onUnload() { - + processCustomers(customers) { + return customers.map(customer => { + const profile = customer.profile || {}; + const sexInfo = SEX_MAP[profile.sex] || SEX_MAP['']; + return { + ...customer, + name: profile.name || '未知姓名', + mobile: profile.mobile || '暂无电话', + sex: profile.sex || '', + sexLabel: sexInfo.label, + sexText: sexInfo.text, + avatar: profile.avatar || '', + avatarText: profile.name ? profile.name[0].toUpperCase() : '客', + birth: profile.birth ? this.formatDate(profile.birth) : '', + createdAt: customer.meta?.createtime ? this.formatDate(customer.meta.createtime) : '', + locationText: this.getLocationText(customer) + }; + }); }, /** - * 页面相关事件处理函数--监听用户下拉动作 + * 获取位置文本 + */ + getLocationText(customer) { + const location = customer.location || {}; + if (location.province || location.city) { + return `${location.province || ''}${location.city || ''}`; + } + return ''; + }, + + /** + * 计算统计数据 + */ + calculateStats(customers) { + const stats = { + total: customers.length, + male: 0, + female: 0 + }; + + customers.forEach(customer => { + const sex = customer.profile?.sex || ''; + if (sex === 'male') stats.male++; + else if (sex === 'female') stats.female++; + }); + + return stats; + }, + + /** + * 加载客户列表 + */ + loadCustomerList(isRefresh = false) { + if (this.data.isLoading || this.data.isLoadingMore) return; + + const { page, pageSize, searchKey } = this.data; + this.setData({ + isLoading: !isRefresh && page === 1, + isLoadingMore: isRefresh && page > 1 + }); + + const params = { + page, + pageSize + }; + + if (searchKey && searchKey.trim()) { + params.keyword = searchKey.trim(); + } + + API.user.userList(params) + .then(res => { + if (res.code !== 0) { + wx.showToast({ title: res.message || '获取客户列表失败', icon: 'none' }); + this.setData({ isLoading: false, isLoadingMore: false, isRefreshing: false }); + return; + } + + const data = res.data || {}; + const list = data.users || []; + const total = data?.users?.length || 0; + + const processedCustomers = this.processCustomers(list); + + let allCustomers = isRefresh && page > 1 + ? [...this.data.customerList, ...processedCustomers] + : processedCustomers; + + // 如果有搜索条件,在前端过滤 + if (searchKey && searchKey.trim()) { + const keyword = searchKey.trim().toLowerCase(); + allCustomers = allCustomers.filter(customer => { + return customer.name.toLowerCase().includes(keyword) || + customer.mobile.includes(keyword); + }); + } + + const stats = this.calculateStats(allCustomers); + + this.setData({ + customerList: allCustomers, + stats, + isLoading: false, + isLoadingMore: false, + isRefreshing: false, + hasMore: allCustomers.length < total + }); + }) + .catch(err => { + console.error('获取客户列表失败', err); + wx.showToast({ title: '网络错误,请重试', icon: 'none' }); + this.setData({ isLoading: false, isLoadingMore: false, isRefreshing: false }); + }); + }, + + /** + * 刷新数据 + */ + refreshData() { + this.setData({ page: 1, hasMore: true }, () => { + this.loadCustomerList(); + }); + }, + + /** + * 搜索输入 + */ + onSearchInput(e) { + this.setData({ searchKey: e.detail.value }); + }, + + /** + * 搜索确认 + */ + onSearch() { + this.setData({ page: 1, hasMore: true, customerList: [] }, () => { + this.loadCustomerList(); + }); + }, + + /** + * 清除搜索 + */ + onClearSearch() { + if (this.data.searchKey) { + this.setData({ searchKey: '', page: 1, hasMore: true, customerList: [] }, () => { + this.loadCustomerList(); + }); + } + }, + + /** + * 客户详情 + */ + onCustomerDetail(e) { + const { id } = e.currentTarget.dataset; + wx.navigateTo({ + url: `/pages/customer/detail?id=${id}` + }); + }, + + /** + * 拨打电话 + */ + onCallPhone(e) { + const { phone } = e.currentTarget.dataset; + if (!phone) { + wx.showToast({ title: '暂无电话号码', icon: 'none' }); + return; + } + wx.makePhoneCall({ + phoneNumber: phone, + fail: () => { + wx.showToast({ title: '拨打电话失败', icon: 'none' }); + } + }); + }, + + /** + * 下拉刷新 */ onPullDownRefresh() { - + this.setData({ isRefreshing: true, page: 1, hasMore: true }, () => { + this.loadCustomerList(); + }); + wx.stopPullDownRefresh(); }, /** - * 页面上拉触底事件的处理函数 + * 上拉加载更多 */ onReachBottom() { + if (!this.data.hasMore || this.data.isLoadingMore) return; + this.setData({ page: this.data.page + 1 }, () => { + this.loadCustomerList(true); + }); }, /** * 用户点击右上角分享 */ onShareAppMessage() { - + return { + title: '客户管理', + path: '/pages/customer/index' + }; } -}) \ No newline at end of file +}); diff --git a/pages/customer/index.json b/pages/customer/index.json index 8835af0..bb0f45e 100644 --- a/pages/customer/index.json +++ b/pages/customer/index.json @@ -1,3 +1,11 @@ { - "usingComponents": {} -} \ No newline at end of file + "navigationBarTitleText": "客户管理", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#ffffff", + "usingComponents": { + "t-empty": "tdesign-miniprogram/empty/empty", + "t-loading": "tdesign-miniprogram/loading/loading", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} diff --git a/pages/customer/index.less b/pages/customer/index.less index 68962f1..21fd4b1 100644 --- a/pages/customer/index.less +++ b/pages/customer/index.less @@ -1 +1,328 @@ -/* pages/customer/index.wxss */ \ No newline at end of file +/* pages/customer/index.less */ + +// 颜色变量 +@bg-primary: #f5f6fa; +@bg-secondary: #ffffff; +@bg-card: #ffffff; +@accent-primary: #4c6ef5; +@accent-secondary: #6b7aff; +@accent-gradient-start: #4c6ef5; +@accent-gradient-end: #748ffc; +@text-primary: #1a1a2e; +@text-secondary: #6b7280; +@text-muted: #9ca3af; +@border-color: #e5e7eb; +@male-color: #4c6ef5; +@female-color: #ff6b6b; +@divider-color: #f3f4f6; + +page { + background-color: @bg-primary; + color: @text-primary; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +.customer-page { + display: flex; + flex-direction: column; + height: 100vh; + background-color: @bg-primary; +} + +// ========== 搜索区域 ========== +.search-section { + display: flex; + align-items: center; + gap: 16rpx; + padding: 20rpx 24rpx; + background-color: @bg-secondary; + border-bottom: 1rpx solid @border-color; +} + +.search-bar { + flex: 1; + display: flex; + align-items: center; + gap: 12rpx; + padding: 16rpx 24rpx; + background-color: @bg-primary; + border-radius: 32rpx; + border: 1rpx solid @border-color; +} + +.search-input { + flex: 1; + font-size: 28rpx; + color: @text-primary; + height: 40rpx; + line-height: 40rpx; + + &::placeholder { + color: @text-muted; + } +} + +.search-clear { + display: flex; + align-items: center; + justify-content: center; + padding: 4rpx; +} + +.search-btn { + padding: 16rpx 28rpx; + background: linear-gradient(135deg, @accent-gradient-start, @accent-gradient-end); + color: #ffffff; + font-size: 26rpx; + font-weight: 500; + border-radius: 32rpx; + white-space: nowrap; + box-shadow: 0 4rpx 16rpx rgba(76, 110, 245, 0.25); + + &:active { + opacity: 0.9; + transform: scale(0.98); + } +} + +// ========== 统计区域 ========== +.stats-section { + padding: 20rpx 24rpx 0; +} + +.stats-card { + display: flex; + align-items: center; + justify-content: space-around; + padding: 28rpx 24rpx; + background-color: @bg-card; + border-radius: 24rpx; + border: 1rpx solid @border-color; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); +} + +.stats-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 8rpx; +} + +.stats-value { + font-size: 40rpx; + font-weight: 700; + color: @text-primary; + + &.male { + color: @male-color; + } + + &.female { + color: @female-color; + } +} + +.stats-label { + font-size: 24rpx; + color: @text-secondary; +} + +.stats-divider { + width: 1rpx; + height: 60rpx; + background-color: @divider-color; +} + +// ========== 客户列表区域 ========== +.customer-list { + flex: 1; + overflow: hidden; + padding: 20rpx 24rpx 0; +} + +.customer-scroll { + height: 100%; +} + +.loading-container, +.empty-container { + display: flex; + align-items: center; + justify-content: center; + padding: 200rpx 40rpx; +} + +.loading-text { + color: @text-secondary !important; + font-size: 26rpx !important; +} + +.empty-text { + color: @text-secondary !important; + font-size: 28rpx !important; +} + +.empty-action { + margin-top: 30rpx; + padding: 16rpx 48rpx; + background: linear-gradient(135deg, @accent-gradient-start, @accent-gradient-end); + color: #ffffff; + font-size: 28rpx; + border-radius: 32rpx; + display: inline-block; +} + +// ========== 客户卡片 ========== +.customer-cards { + padding-bottom: 40rpx; +} + +.customer-card { + display: flex; + align-items: center; + gap: 24rpx; + margin-bottom: 20rpx; + padding: 28rpx; + background-color: @bg-card; + border-radius: 24rpx; + border: 1rpx solid @border-color; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); + transition: transform 0.2s ease, box-shadow 0.2s ease; + + &:active { + transform: scale(0.98); + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); + } +} + +// 头像 +.customer-avatar { + flex-shrink: 0; +} + +.avatar-img { + width: 96rpx; + height: 96rpx; + border-radius: 50%; + object-fit: cover; +} + +.avatar-placeholder { + width: 96rpx; + height: 96rpx; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, @accent-gradient-start, @accent-gradient-end); + box-shadow: 0 4rpx 12rpx rgba(76, 110, 245, 0.25); + + &.male { + background: linear-gradient(135deg, #4c6ef5, #748ffc); + } + + &.female { + background: linear-gradient(135deg, #ff6b6b, #ff8787); + } +} + +.avatar-text { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; +} + +// 客户信息 +.customer-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 12rpx; + min-width: 0; +} + +.info-row { + display: flex; + align-items: center; + gap: 16rpx; + flex-wrap: wrap; +} + +.customer-name { + font-size: 32rpx; + font-weight: 600; + color: @text-primary; +} + +.gender-tag { + display: inline-flex; + align-items: center; + padding: 4rpx 14rpx; + border-radius: 10rpx; + font-size: 22rpx; + font-weight: 500; + + &.male { + background-color: rgba(76, 110, 245, 0.1); + color: @male-color; + } + + &.female { + background-color: rgba(255, 107, 107, 0.1); + color: @female-color; + } +} + +.customer-date { + font-size: 22rpx; + color: @text-muted; + margin-left: auto; +} + +.customer-phone { + font-size: 26rpx; + color: @text-secondary; +} + +.customer-location { + font-size: 24rpx; + color: @text-muted; +} + +// 操作按钮 +.customer-action { + flex-shrink: 0; +} + +.action-btn { + display: flex; + align-items: center; + justify-content: center; + width: 72rpx; + height: 72rpx; + border-radius: 50%; + background-color: rgba(76, 110, 245, 0.08); + transition: all 0.2s ease; + + &:active { + background-color: rgba(76, 110, 245, 0.15); + transform: scale(0.95); + } + + &.call { + background-color: rgba(76, 110, 245, 0.08); + } +} + +// ========== 加载更多 ========== +.load-more { + display: flex; + align-items: center; + justify-content: center; + padding: 40rpx 20rpx; +} + +.no-more { + font-size: 24rpx; + color: @text-muted; +} diff --git a/pages/customer/index.wxml b/pages/customer/index.wxml index 2a223d3..73c51a6 100644 --- a/pages/customer/index.wxml +++ b/pages/customer/index.wxml @@ -1,2 +1,116 @@ -pages/customer/index.wxml \ No newline at end of file + + + + + + + + + + + + 搜索 + + + + + + + {{stats.total}} + 客户总数 + + + + {{stats.male}} + + + + + {{stats.female}} + + + + + + + + + + + + + + + + + + 刷新试试 + + + + + + + + + + + + {{item.avatarText}} + + + + + + + {{item.name}} + + {{item.sexLabel}} + + {{item.createdAt}} + + + {{item.mobile}} + {{item.locationText}} + + + + + + + + + + + + + + + 没有更多了 + + + + + diff --git a/pages/home/index.js b/pages/home/index.js index 347a4db..100bf40 100644 --- a/pages/home/index.js +++ b/pages/home/index.js @@ -1,66 +1,90 @@ // pages/home/index.js +const API = require('../../utils/api.js') + Page({ - - /** - * 页面的初始数据 - */ data: { - + todayCount: 0, + pendingCount: 0, + completedCount: 0, + menuList: [ + { icon: '/images/icon_order.png', name: '订单管理', url: '/pages/order/index' }, + { icon: '/images/icon_patient.png', name: '患者管理', url: '/pages/patient/index' }, + { icon: '/images/icon_escort.png', name: '陪诊员管理', url: '/pages/escort/index' }, + { icon: '/images/icon_schedule.png', name: '排班管理', url: '/pages/schedule/index' }, + { icon: '/images/icon_stats.png', name: '数据统计', url: '/pages/stats/index' }, + { icon: '/images/icon_setting.png', name: '系统设置', url: '/pages/setting/index' } + ], + todayOrders: [], + statusMap: { + pending: '待确认', + confirmed: '已确认', + in_progress: '进行中', + completed: '已完成', + cancelled: '已取消' + } }, - /** - * 生命周期函数--监听页面加载 - */ - onLoad(options) { - - }, - - /** - * 生命周期函数--监听页面初次渲染完成 - */ - onReady() { - - }, - - /** - * 生命周期函数--监听页面显示 - */ - onShow() { - - }, - - /** - * 生命周期函数--监听页面隐藏 - */ - onHide() { - - }, - - /** - * 生命周期函数--监听页面卸载 - */ - onUnload() { - - }, - - /** - * 页面相关事件处理函数--监听用户下拉动作 - */ - onPullDownRefresh() { - - }, - - /** - * 页面上拉触底事件的处理函数 - */ - onReachBottom() { - - }, - - /** - * 用户点击右上角分享 - */ onShareAppMessage() { + return { + title: '暖橙陪诊后台', // 转发标题 + path: '/pages/home/index', + } + }, + onShareTimeline: function () { + return { + title: '暖橙陪诊后台', + } + }, + + onLoad(options) { + this.getTodayOrders(); + this.getStats(); + }, + + onShow() { + this.getTodayOrders(); + this.getStats(); + }, + + async getStats() { + const today = new Date().toISOString().substring(0, 10); + const [todayRes, pendingRes, completedRes] = await Promise.all([ + API.escort.getMyRecords({ appointmentDate: today }), + API.escort.getMyRecords({ status: ['pending', 'confirmed'] }), + API.escort.getMyRecords({ status: ['completed'] }) + ]); + this.setData({ + todayCount: todayRes.code === 0 ? (todayRes.data.records || []).length : 0, + pendingCount: pendingRes.code === 0 ? (pendingRes.data.records || []).length : 0, + completedCount: completedRes.code === 0 ? (completedRes.data.records || []).length : 0, + }); + }, + + async getTodayOrders() { + const res = await API.escort.getMyRecords({ + appointmentDate: new Date().toISOString().substring(0, 10), + }); + if (res.code == 0) { + const records = (res.data.records || []).map(item => { + if (item.schedule && item.schedule.date) { + const d = new Date(item.schedule.date); + item.schedule.date = d.toISOString().substring(0, 10) + ' ' + d.toTimeString().substring(0, 5); + } + return item; + }); + this.setData({ todayOrders: records }); + } + }, + + navigateTo(e) { + const url = e.currentTarget.dataset.url + wx.navigateTo({ url }) + }, + + viewAllOrders() { + wx.navigateTo({ + url: '/pages/order/index' + }) } -}) \ No newline at end of file +}) diff --git a/pages/home/index.wxml b/pages/home/index.wxml index dfac0a3..f3d6db3 100644 --- a/pages/home/index.wxml +++ b/pages/home/index.wxml @@ -1,2 +1,69 @@ -pages/home/index.wxml \ No newline at end of file + + + + + {{todayCount}} + 今日订单 + + + {{pendingCount}} + 待处理 + + + {{completedCount}} + 已完成 + + + + + + 功能菜单 + + + + {{item.name[0]}} + + {{item.name}} + + + + + + + + 今日订单 + 查看全部 > + + + + + {{item._id}} + {{statusMap[item.status] || item.status}} + + + + 患者 + {{item.patient.name}} + + + 医院 + {{item.hospital.name}} · {{item.hospital.department}} + + + 时间 + {{item.schedule.date}} + + + 服务 + {{item.escort.serviceName}} + + + 费用 + ¥{{item.payment.totalFee}} + + + + + + diff --git a/pages/home/index.wxss b/pages/home/index.wxss index b5afb04..0b4bd9c 100644 --- a/pages/home/index.wxss +++ b/pages/home/index.wxss @@ -1 +1,208 @@ -/* pages/home/index.wxss */ \ No newline at end of file +/* pages/home/index.wxss */ +.container { + padding: 20rpx; + background-color: #f5f6fa; + min-height: 100vh; +} + +/* 统计卡片 */ +.stats-section { + display: flex; + justify-content: space-between; + margin-bottom: 20rpx; +} + +.stats-card { + flex: 1; + background: #fff; + border-radius: 16rpx; + padding: 30rpx 0; + margin: 0 10rpx; + text-align: center; + box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04); +} + +.stats-card:first-child { + margin-left: 0; +} + +.stats-card:last-child { + margin-right: 0; +} + +.stats-num { + display: block; + font-size: 48rpx; + font-weight: 600; + color: #2c3e50; + margin-bottom: 8rpx; +} + +.stats-label { + display: block; + font-size: 26rpx; + color: #7f8c8d; +} + +/* 功能菜单 */ +.menu-section { + background: #fff; + border-radius: 16rpx; + padding: 30rpx; + margin-bottom: 20rpx; + box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04); +} + +.section-title { + font-size: 32rpx; + font-weight: 600; + color: #2c3e50; + margin-bottom: 24rpx; +} + +.menu-grid { + display: flex; + flex-wrap: wrap; +} + +.menu-item { + width: 25%; + display: flex; + flex-direction: column; + align-items: center; + padding: 20rpx 0; +} + +.menu-icon { + width: 88rpx; + height: 88rpx; + background: #e8f4fd; + border-radius: 20rpx; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 12rpx; +} + +.icon-text { + font-size: 36rpx; + color: #3498db; + font-weight: 600; +} + +.menu-name { + font-size: 26rpx; + color: #555; +} + +/* 今日订单 */ +.order-section { + background: #fff; + border-radius: 16rpx; + padding: 30rpx; + box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.04); +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24rpx; +} + +.section-header .section-title { + margin-bottom: 0; +} + +.view-all { + font-size: 26rpx; + color: #3498db; +} + +.order-list { + display: flex; + flex-direction: column; + gap: 20rpx; +} + +.order-item { + background: #f8f9fa; + border-radius: 12rpx; + padding: 24rpx; +} + +.order-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16rpx; +} + +.order-id { + font-size: 26rpx; + color: #7f8c8d; +} + +.order-status { + font-size: 24rpx; + padding: 4rpx 16rpx; + border-radius: 8rpx; +} + +.status-pending { + background: #fff3e0; + color: #f39c12; +} + +.status-in_progress { + background: #e8f5e9; + color: #27ae60; +} + +.status-confirmed { + background: #e3f2fd; + color: #2980b9; +} + +.status-pending { + background: #fff3e0; + color: #f39c12; +} + +.status-completed { + background: #f3e5f5; + color: #8e44ad; +} + +.status-cancelled { + background: #fafafa; + color: #95a5a6; +} + +.order-info { + display: flex; + flex-direction: column; + gap: 10rpx; +} + +.info-row { + display: flex; + align-items: center; +} + +.info-label { + font-size: 26rpx; + color: #95a5a6; + width: 80rpx; + flex-shrink: 0; +} + +.info-value { + font-size: 28rpx; + color: #2c3e50; +} + +.fee-value { + color: #e74c3c; + font-weight: 600; +} diff --git a/pages/order/index.js b/pages/order/index.js index ca8ecb5..dfea30d 100644 --- a/pages/order/index.js +++ b/pages/order/index.js @@ -1,4 +1,5 @@ // pages/order/index.js +const API = require('../../utils/api.js') // 状态映射配置 const STATUS_MAP = { @@ -19,322 +20,6 @@ const STATUS_FILTERS = [ { label: '已取消', value: 'cancelled', count: 0 } ]; -// Mock 数据 - 基于 escort_record.js 的数据结构 -const MOCK_ORDERS = [ - { - _id: 'ORD20240530001', - orderNo: '20240530001', - userId: 'user001', - patient: { - name: '张三', - mobile: '138****1234', - sex: 'male', - age: 65, - idnumber: '310***********1234' - }, - escort: { - serviceId: 1, - serviceName: '全程陪诊服务' - }, - hospital: { - province: '上海', - name: '复旦大学附属中山医院', - address: '上海市徐汇区枫林路180号', - department: '心内科', - doctor: '李医生', - medicalRecordNo: 'MR202405001' - }, - schedule: { - date: '2024-06-01T09:00:00.000Z', - dateText: '06月01日', - startTime: '09:00', - endTime: '12:00', - duration: 180 - }, - attendant: { - id: 'att001', - name: '王陪诊', - sex: 'female' - }, - payment: { - totalFee: 298, - paidFee: 298, - status: 'paid' - }, - notes: { - patientNote: '患者行动不便,需要轮椅', - escortNote: '', - medicalSummary: '' - }, - status: 'pending', - statusText: '待确认', - meta: { - createtime: '2024-05-30T10:00:00.000Z', - updatetime: '2024-05-30T10:00:00.000Z' - } - }, - { - _id: 'ORD20240529002', - orderNo: '20240529002', - userId: 'user002', - patient: { - name: '李四', - mobile: '139****5678', - sex: 'female', - age: 42, - idnumber: '310***********5678' - }, - escort: { - serviceId: 2, - serviceName: '挂号陪诊服务' - }, - hospital: { - province: '上海', - name: '上海交通大学医学院附属瑞金医院', - address: '上海市黄浦区瑞金二路197号', - department: '内分泌科', - doctor: '张医生', - medicalRecordNo: 'MR202405002' - }, - schedule: { - date: '2024-05-31T14:00:00.000Z', - dateText: '05月31日', - startTime: '14:00', - endTime: '16:00', - duration: 120 - }, - attendant: { - id: 'att002', - name: '赵陪诊', - sex: 'male' - }, - payment: { - totalFee: 198, - paidFee: 198, - status: 'paid' - }, - notes: { - patientNote: '需要帮忙取药', - escortNote: '', - medicalSummary: '' - }, - status: 'confirmed', - statusText: '已确认', - meta: { - createtime: '2024-05-29T08:30:00.000Z', - updatetime: '2024-05-29T15:00:00.000Z' - } - }, - { - _id: 'ORD20240528003', - orderNo: '20240528003', - userId: 'user003', - patient: { - name: '王五', - mobile: '137****9012', - sex: 'male', - age: 78, - idnumber: '310***********9012' - }, - escort: { - serviceId: 1, - serviceName: '全程陪诊服务' - }, - hospital: { - province: '上海', - name: '上海市第六人民医院', - address: '上海市徐汇区宜山路600号', - department: '骨科', - doctor: '刘医生', - medicalRecordNo: 'MR202405003' - }, - schedule: { - date: '2024-05-30T08:30:00.000Z', - dateText: '05月30日', - startTime: '08:30', - endTime: '11:30', - duration: 180 - }, - attendant: { - id: 'att003', - name: '陈陪诊', - sex: 'female' - }, - payment: { - totalFee: 398, - paidFee: 200, - status: 'partial' - }, - notes: { - patientNote: '听力不好,请大声说话', - escortNote: '已接到患者,正在前往医院', - medicalSummary: '' - }, - status: 'in_progress', - statusText: '进行中', - meta: { - createtime: '2024-05-28T16:00:00.000Z', - updatetime: '2024-05-30T08:35:00.000Z' - } - }, - { - _id: 'ORD20240525004', - orderNo: '20240525004', - userId: 'user004', - patient: { - name: '赵六', - mobile: '136****3456', - sex: 'female', - age: 55, - idnumber: '310***********3456' - }, - escort: { - serviceId: 3, - serviceName: '检查陪诊服务' - }, - hospital: { - province: '上海', - name: '华东医院', - address: '上海市静安区延安西路221号', - department: '体检中心', - doctor: '', - medicalRecordNo: 'MR202405004' - }, - schedule: { - date: '2024-05-28T07:30:00.000Z', - dateText: '05月28日', - startTime: '07:30', - endTime: '11:00', - duration: 210 - }, - attendant: { - id: 'att004', - name: '孙陪诊', - sex: 'male' - }, - payment: { - totalFee: 258, - paidFee: 258, - status: 'paid' - }, - notes: { - patientNote: '需要空腹检查', - escortNote: '服务完成,患者已安全送回家', - medicalSummary: '完成全身检查,各项指标正常' - }, - status: 'completed', - statusText: '已完成', - meta: { - createtime: '2024-05-25T09:00:00.000Z', - updatetime: '2024-05-28T11:30:00.000Z' - } - }, - { - _id: 'ORD20240524005', - orderNo: '20240524005', - userId: 'user005', - patient: { - name: '钱七', - mobile: '135****7890', - sex: 'male', - age: 33, - idnumber: '310***********7890' - }, - escort: { - serviceId: 2, - serviceName: '挂号陪诊服务' - }, - hospital: { - province: '上海', - name: '上海市第一人民医院', - address: '上海市虹口区武进路85号', - department: '眼科', - doctor: '周医生', - medicalRecordNo: 'MR202405005' - }, - schedule: { - date: '2024-05-27T10:00:00.000Z', - dateText: '05月27日', - startTime: '10:00', - endTime: '11:00', - duration: 60 - }, - attendant: { - id: '', - name: '', - sex: 'none' - }, - payment: { - totalFee: 128, - paidFee: 0, - status: 'unpaid' - }, - notes: { - patientNote: '临时有事,需要取消', - escortNote: '', - medicalSummary: '' - }, - status: 'cancelled', - statusText: '已取消', - meta: { - createtime: '2024-05-24T14:00:00.000Z', - updatetime: '2024-05-26T09:00:00.000Z' - } - }, - { - _id: 'ORD20240523006', - orderNo: '20240523006', - userId: 'user006', - patient: { - name: '孙八', - mobile: '134****2468', - sex: 'female', - age: 60, - idnumber: '310***********2468' - }, - escort: { - serviceId: 1, - serviceName: '全程陪诊服务' - }, - hospital: { - province: '上海', - name: '上海中医药大学附属龙华医院', - address: '上海市徐汇区宛平南路725号', - department: '中医内科', - doctor: '吴医生', - medicalRecordNo: 'MR202405006' - }, - schedule: { - date: '2024-06-02T08:00:00.000Z', - dateText: '06月02日', - startTime: '08:00', - endTime: '11:00', - duration: 180 - }, - attendant: { - id: 'att005', - name: '周陪诊', - sex: 'female' - }, - payment: { - totalFee: 328, - paidFee: 328, - status: 'paid' - }, - notes: { - patientNote: ' prefer 女陪诊员', - escortNote: '', - medicalSummary: '' - }, - status: 'pending', - statusText: '待确认', - meta: { - createtime: '2024-05-23T11:00:00.000Z', - updatetime: '2024-05-23T11:00:00.000Z' - } - } -]; - Page({ /** @@ -376,7 +61,6 @@ Page({ * 生命周期函数--监听页面显示 */ onShow() { - // 页面显示时刷新数据 if (this.data.orderList.length > 0) { this.refreshData(); } @@ -468,7 +152,10 @@ Page({ return orders.map(order => ({ ...order, statusText: STATUS_MAP[order.status]?.text || order.status, - 'schedule.dateText': this.formatDate(order.schedule.date) + schedule: { + ...order.schedule, + dateText: this.formatDate(order.schedule?.date) + } })); }, @@ -482,40 +169,53 @@ Page({ this.setData({ isLoading: !isRefresh, isLoadingMore: isRefresh && page > 1 }); - // 模拟 API 请求延迟 - setTimeout(() => { - // 筛选数据 - let filteredOrders = MOCK_ORDERS; - if (currentStatus) { - filteredOrders = MOCK_ORDERS.filter(order => order.status === currentStatus); - } + const params = { + page, + pageSize, + status: 'pending,confirmed,in_progress,completed,cancelled' + }; - // 分页 - const start = (page - 1) * pageSize; - const end = start + pageSize; - const pageData = filteredOrders.slice(start, end); + API.escort.getMyRecords(params) + .then(res => { + if (res.code !== 0) { + wx.showToast({ title: res.message || '获取订单失败', icon: 'none' }); + this.setData({ isLoading: false, isLoadingMore: false, isRefreshing: false }); + return; + } - // 处理数据 - const processedOrders = this.processOrders(pageData); + const data = res.data || {}; + const list = data.records || []; + let total = list.length || 0; - // 计算统计 - const stats = this.calculateStats(MOCK_ORDERS); - this.updateFilterCounts(stats); + const processedOrders = this.processOrders(list); - // 更新列表 - const orderList = isRefresh && page > 1 - ? [...this.data.orderList, ...processedOrders] - : processedOrders; + const allOrders = isRefresh && page > 1 + ? [...this.data.orderList, ...processedOrders] + : processedOrders; - this.setData({ - orderList, - stats, - isLoading: false, - isLoadingMore: false, - isRefreshing: false, - hasMore: end < filteredOrders.length + const stats = this.calculateStats(allOrders); + this.updateFilterCounts(stats); + + const { currentStatus } = this.data; + const orderList = currentStatus + ? allOrders.filter(order => order.status === currentStatus) + : allOrders; + total = orderList.length; + + this.setData({ + orderList, + stats, + isLoading: false, + isLoadingMore: false, + isRefreshing: false, + hasMore: orderList.length < total + }); + }) + .catch(err => { + console.error('获取订单列表失败', err); + wx.showToast({ title: '网络错误,请重试', icon: 'none' }); + this.setData({ isLoading: false, isLoadingMore: false, isRefreshing: false }); }); - }, 600); }, /** @@ -562,9 +262,6 @@ Page({ const order = this.data.orderList.find(item => item._id === id); if (!order) return; - // 阻止冒泡,防止触发卡片点击 - e.stopPropagation(); - const actionMap = { confirm: { title: '确认订单', content: '确认接受此订单?', nextStatus: 'confirmed' }, cancel: { title: '取消订单', content: '确定要取消此订单?', nextStatus: 'cancelled' }, @@ -589,7 +286,6 @@ Page({ confirmColor: '#4c6ef5', success: (res) => { if (res.confirm) { - // 更新订单状态 this.updateOrderStatus(id, actionConfig.nextStatus); } } @@ -602,34 +298,38 @@ Page({ updateOrderStatus(id, newStatus) { wx.showLoading({ title: '处理中...' }); - // 模拟 API 请求 - setTimeout(() => { - const orderList = this.data.orderList.map(order => { - if (order._id === id) { - return { - ...order, - status: newStatus, - statusText: STATUS_MAP[newStatus]?.text || newStatus, - 'meta.updatetime': new Date().toISOString() - }; + API.escort.updateStatus(id, { status: newStatus }) + .then(res => { + if (res.code !== 0) { + wx.showToast({ title: res.message || '操作失败', icon: 'none' }); + wx.hideLoading(); + return; } - return order; - }); - // 重新计算统计 - const allOrders = MOCK_ORDERS.map(order => { - if (order._id === id) { - return { ...order, status: newStatus }; - } - return order; - }); - const stats = this.calculateStats(allOrders); - this.updateFilterCounts(stats); + const orderList = this.data.orderList.map(order => { + if (order._id === id) { + return { + ...order, + status: newStatus, + statusText: STATUS_MAP[newStatus]?.text || newStatus, + 'meta.updatetime': new Date().toISOString() + }; + } + return order; + }); - this.setData({ orderList, stats }); - wx.hideLoading(); - wx.showToast({ title: '操作成功', icon: 'success' }); - }, 500); + const stats = this.calculateStats(orderList); + this.updateFilterCounts(stats); + + this.setData({ orderList, stats }); + wx.hideLoading(); + wx.showToast({ title: '操作成功', icon: 'success' }); + }) + .catch(err => { + console.error('更新订单状态失败', err); + wx.showToast({ title: '网络错误,请重试', icon: 'none' }); + wx.hideLoading(); + }); }, /** diff --git a/pages/order/index.json b/pages/order/index.json index 7685b21..642195d 100644 --- a/pages/order/index.json +++ b/pages/order/index.json @@ -1,8 +1,8 @@ { "navigationBarTitleText": "订单管理", - "navigationBarBackgroundColor": "#1a1f3c", - "navigationBarTextStyle": "white", - "backgroundColor": "#0f1535", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#ffffff", "usingComponents": { "t-tabs": "tdesign-miniprogram/tabs/tabs", "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", diff --git a/pages/order/index.less b/pages/order/index.less index 912e611..01b8e48 100644 --- a/pages/order/index.less +++ b/pages/order/index.less @@ -1,24 +1,23 @@ /* pages/order/index.less */ -// 颜色变量 -@bg-primary: #0f1535; -@bg-secondary: #1a1f3c; -@bg-card: #1e2548; -@bg-card-hover: #252d5a; +// 颜色变量 - 参考图浅色社交风格 +@bg-primary: #f5f6fa; +@bg-secondary: #ffffff; +@bg-card: #ffffff; @accent-primary: #4c6ef5; @accent-secondary: #6b7aff; @accent-gradient-start: #4c6ef5; @accent-gradient-end: #748ffc; -@text-primary: #ffffff; -@text-secondary: #a0a8d0; -@text-muted: #6b7298; -@border-color: #2a3366; +@text-primary: #1a1a2e; +@text-secondary: #6b7280; +@text-muted: #9ca3af; +@border-color: #e5e7eb; @status-pending: #f59f00; @status-confirmed: #4c6ef5; @status-in-progress: #20c997; @status-completed: #51cf66; @status-cancelled: #ff6b6b; -@divider-color: #2a3366; +@divider-color: #f3f4f6; page { background-color: @bg-primary; @@ -33,79 +32,11 @@ page { background-color: @bg-primary; } -// ========== 顶部统计区域 ========== -.header-section { - position: relative; - padding: 30rpx 30rpx 40rpx; - overflow: hidden; - - .header-bg { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(135deg, @accent-gradient-start 0%, @accent-gradient-end 100%); - border-radius: 0 0 40rpx 40rpx; - opacity: 0.15; - } - - &::before { - content: ''; - position: absolute; - top: -100rpx; - right: -100rpx; - width: 300rpx; - height: 300rpx; - background: radial-gradient(circle, rgba(76, 110, 245, 0.2) 0%, transparent 70%); - border-radius: 50%; - } -} - -.stats-container { - position: relative; - display: flex; - align-items: center; - justify-content: space-around; - background: rgba(30, 37, 72, 0.8); - backdrop-filter: blur(20rpx); - border-radius: 24rpx; - padding: 30rpx 20rpx; - border: 1rpx solid rgba(107, 122, 255, 0.1); - box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.2); -} - -.stat-item { - display: flex; - flex-direction: column; - align-items: center; - flex: 1; -} - -.stat-value { - font-size: 40rpx; - font-weight: 700; - color: @text-primary; - line-height: 1.2; - margin-bottom: 8rpx; -} - -.stat-label { - font-size: 24rpx; - color: @text-secondary; - line-height: 1.4; -} - -.stat-divider { - width: 1rpx; - height: 60rpx; - background: linear-gradient(to bottom, transparent, @divider-color, transparent); -} - // ========== 筛选区域 ========== .filter-section { padding: 20rpx 0; - background-color: @bg-primary; + background-color: @bg-secondary; + border-bottom: 1rpx solid @border-color; } .filter-scroll { @@ -114,28 +45,33 @@ page { .filter-list { display: inline-flex; - padding: 0 20rpx; - gap: 16rpx; + padding: 0 24rpx; + gap: 20rpx; } .filter-item { display: inline-flex; align-items: center; - padding: 16rpx 28rpx; - background-color: @bg-card; + padding: 16rpx 32rpx; + background-color: @bg-primary; border-radius: 32rpx; - border: 1rpx solid transparent; + border: 1rpx solid @border-color; transition: all 0.3s ease; &.active { background: linear-gradient(135deg, @accent-gradient-start, @accent-gradient-end); border-color: transparent; - box-shadow: 0 4rpx 16rpx rgba(76, 110, 245, 0.3); + box-shadow: 0 4rpx 16rpx rgba(76, 110, 245, 0.25); .filter-text { - color: @text-primary; + color: #ffffff; font-weight: 600; } + + .filter-badge { + background-color: #ffffff; + color: @accent-primary; + } } } @@ -156,7 +92,7 @@ page { background-color: @status-cancelled; border-radius: 16rpx; font-size: 20rpx; - color: @text-primary; + color: #ffffff; font-weight: 600; } @@ -168,7 +104,7 @@ page { .order-scroll { height: 100%; - padding: 0 20rpx; + padding: 0 24rpx; } .loading-container, @@ -193,7 +129,7 @@ page { margin-top: 30rpx; padding: 16rpx 48rpx; background: linear-gradient(135deg, @accent-gradient-start, @accent-gradient-end); - color: @text-primary; + color: #ffffff; font-size: 28rpx; border-radius: 32rpx; display: inline-block; @@ -201,7 +137,7 @@ page { // ========== 订单卡片 ========== .order-cards { - padding-bottom: 40rpx; + padding: 24rpx 0 40rpx; } .order-card { @@ -209,34 +145,13 @@ page { background-color: @bg-card; border-radius: 24rpx; border: 1rpx solid @border-color; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); overflow: hidden; transition: transform 0.2s ease, box-shadow 0.2s ease; &:active { transform: scale(0.98); - background-color: @bg-card-hover; - } - - // 状态左边框 - &.pending { - border-left: 4rpx solid @status-pending; - } - - &.confirmed { - border-left: 4rpx solid @status-confirmed; - } - - &.in_progress { - border-left: 4rpx solid @status-in-progress; - } - - &.completed { - border-left: 4rpx solid @status-completed; - } - - &.cancelled { - border-left: 4rpx solid @status-cancelled; - opacity: 0.85; + box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08); } } @@ -244,7 +159,7 @@ page { display: flex; align-items: center; justify-content: space-between; - padding: 24rpx 28rpx 16rpx; + padding: 28rpx 28rpx 20rpx; } .order-id { @@ -256,38 +171,39 @@ page { .order-id-text { font-size: 24rpx; color: @text-muted; + font-weight: 500; } .status-tag { display: inline-flex; align-items: center; - padding: 6rpx 16rpx; - border-radius: 8rpx; + padding: 8rpx 18rpx; + border-radius: 20rpx; font-size: 22rpx; font-weight: 600; &.pending { - background-color: rgba(245, 159, 0, 0.15); + background-color: rgba(245, 159, 0, 0.1); color: @status-pending; } &.confirmed { - background-color: rgba(76, 110, 245, 0.15); - color: @accent-secondary; + background-color: rgba(76, 110, 245, 0.1); + color: @accent-primary; } &.in_progress { - background-color: rgba(32, 201, 151, 0.15); + background-color: rgba(32, 201, 151, 0.1); color: @status-in-progress; } &.completed { - background-color: rgba(81, 207, 102, 0.15); + background-color: rgba(81, 207, 102, 0.1); color: @status-completed; } &.cancelled { - background-color: rgba(255, 107, 107, 0.15); + background-color: rgba(255, 107, 107, 0.1); color: @status-cancelled; } } @@ -298,72 +214,75 @@ page { } .patient-row { - margin-bottom: 16rpx; + margin-bottom: 20rpx; } .patient-info { display: flex; align-items: center; - gap: 20rpx; + gap: 24rpx; } .patient-avatar { - width: 72rpx; - height: 72rpx; + width: 80rpx; + height: 80rpx; background: linear-gradient(135deg, @accent-gradient-start, @accent-gradient-end); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; + box-shadow: 0 4rpx 12rpx rgba(76, 110, 245, 0.25); } .avatar-text { - font-size: 28rpx; + font-size: 30rpx; font-weight: 600; - color: @text-primary; + color: #ffffff; } .patient-detail { flex: 1; display: flex; flex-direction: column; - gap: 6rpx; + gap: 8rpx; } .patient-name-row { display: flex; align-items: center; - gap: 12rpx; + gap: 14rpx; } .patient-name { - font-size: 30rpx; + font-size: 32rpx; font-weight: 600; color: @text-primary; } .patient-gender { font-size: 22rpx; - padding: 2rpx 10rpx; - border-radius: 6rpx; - background-color: rgba(107, 122, 255, 0.15); - color: @accent-secondary; + padding: 4rpx 12rpx; + border-radius: 10rpx; + background-color: rgba(76, 110, 245, 0.1); + color: @accent-primary; + font-weight: 500; &.male { - background-color: rgba(76, 110, 245, 0.15); + background-color: rgba(76, 110, 245, 0.1); color: @accent-primary; } &.female { - background-color: rgba(255, 107, 107, 0.15); + background-color: rgba(255, 107, 107, 0.1); color: @status-cancelled; } } .patient-age { - font-size: 22rpx; + font-size: 24rpx; color: @text-secondary; + font-weight: 500; } .patient-phone { @@ -373,25 +292,25 @@ page { .info-divider { height: 1rpx; - background: linear-gradient(to right, transparent, @divider-color, transparent); - margin: 16rpx 0; + background-color: @divider-color; + margin: 20rpx 0; } .info-item { display: flex; align-items: flex-start; - gap: 12rpx; - margin-bottom: 12rpx; + gap: 14rpx; + margin-bottom: 14rpx; } .info-content { display: flex; flex-direction: column; - gap: 4rpx; + gap: 6rpx; } .hospital-name { - font-size: 26rpx; + font-size: 28rpx; color: @text-primary; font-weight: 500; } @@ -404,16 +323,19 @@ page { .service-name { font-size: 26rpx; color: @text-secondary; + font-weight: 500; } .time-text { font-size: 26rpx; color: @text-secondary; + font-weight: 500; } .attendant-name { font-size: 26rpx; color: @text-secondary; + font-weight: 500; } // ========== 卡片底部 ========== @@ -421,9 +343,9 @@ page { display: flex; align-items: center; justify-content: space-between; - padding: 20rpx 28rpx 24rpx; - margin-top: 8rpx; - border-top: 1rpx solid rgba(42, 51, 102, 0.5); + padding: 24rpx 28rpx 28rpx; + margin-top: 12rpx; + border-top: 1rpx solid @divider-color; } .fee-section { @@ -438,7 +360,7 @@ page { } .fee-value { - font-size: 36rpx; + font-size: 38rpx; font-weight: 700; color: @status-pending; } @@ -452,7 +374,7 @@ page { display: inline-flex; align-items: center; justify-content: center; - padding: 14rpx 32rpx; + padding: 16rpx 36rpx; border-radius: 28rpx; font-size: 26rpx; font-weight: 500; @@ -466,17 +388,17 @@ page { .btn-primary { background: linear-gradient(135deg, @accent-gradient-start, @accent-gradient-end); - color: @text-primary; - box-shadow: 0 4rpx 16rpx rgba(76, 110, 245, 0.3); + color: #ffffff; + box-shadow: 0 4rpx 16rpx rgba(76, 110, 245, 0.25); } .btn-secondary { - background-color: transparent; + background-color: @bg-primary; color: @text-secondary; border: 1rpx solid @border-color; &:active { - background-color: rgba(107, 122, 255, 0.1); + background-color: rgba(76, 110, 245, 0.05); } } diff --git a/pages/set/index.js b/pages/set/index.js index bb481df..08987c6 100644 --- a/pages/set/index.js +++ b/pages/set/index.js @@ -1,66 +1,222 @@ -// pages/mine/index.js +const API = require('../../utils/api.js') + Page({ - - /** - * 页面的初始数据 - */ data: { - + isLoggedIn: false, + userInfo: null, + phoneNumber: '', + version: '1.0.0', + showLoginPopup: false, + loginForm: { + name: '', + mobile: '' + }, + menuList: [ + { icon: 'user', title: '个人资料', url: '' }, + { icon: 'notification', title: '消息通知', url: '' }, + { icon: 'lock-on', title: '账号安全', url: '' }, + { icon: 'help-circle', title: '帮助中心', url: '' }, + { icon: 'info-circle', title: '关于我们', url: '' }, + ] }, - /** - * 生命周期函数--监听页面加载 - */ - onLoad(options) { + onLoad() { + this.checkLoginStatus() + this.setData({ version: this.getAppVersion() }) + const app = getApp() + app.eventBus.on('user-login', this.onUserLogin) }, - /** - * 生命周期函数--监听页面初次渲染完成 - */ - onReady() { - - }, - - /** - * 生命周期函数--监听页面显示 - */ - onShow() { - - }, - - /** - * 生命周期函数--监听页面隐藏 - */ - onHide() { - - }, - - /** - * 生命周期函数--监听页面卸载 - */ onUnload() { - + const app = getApp() + app.eventBus.off('user-login', this.onUserLogin) }, - /** - * 页面相关事件处理函数--监听用户下拉动作 - */ - onPullDownRefresh() { - + onShow() { + this.checkLoginStatus() }, - /** - * 页面上拉触底事件的处理函数 - */ - onReachBottom() { - + onUserLogin(user) { + this.setData({ + isLoggedIn: true, + userInfo: user, + phoneNumber: this.maskPhoneNumber(user.mobile || '') + }) }, - /** - * 用户点击右上角分享 - */ - onShareAppMessage() { + checkLoginStatus() { + const app = getApp() + const user = app.globalData.user + if (user && user.security && user.security.token) { + this.setData({ + isLoggedIn: true, + userInfo: user, + phoneNumber: this.maskPhoneNumber(user.mobile || '') + }) + } + }, + maskPhoneNumber(phone) { + if (!phone || phone.length < 7) return phone + return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') + }, + + getAppVersion() { + const accountInfo = wx.getAccountInfoSync() + return accountInfo.miniProgram.version || '1.0.0' + }, + + onShowLoginPopup() { + this.setData({ showLoginPopup: true }) + }, + + onCloseLoginPopup() { + this.setData({ showLoginPopup: false, loginForm: { name: '', mobile: '' } }) + }, + + onNameInput(e) { + this.setData({ 'loginForm.name': e.detail.value }) + }, + + onMobileInput(e) { + this.setData({ 'loginForm.mobile': e.detail.value }) + }, + + onLoginSubmit() { + const { name, mobile } = this.data.loginForm + + if (!name.trim()) { + wx.showToast({ title: '请输入姓名', icon: 'none' }) + return + } + + if (!mobile.trim()) { + wx.showToast({ title: '请输入手机号', icon: 'none' }) + return + } + + const phoneReg = /^1[3-9]\d{9}$/ + if (!phoneReg.test(mobile.trim())) { + wx.showToast({ title: '手机号格式不正确', icon: 'none' }) + return + } + + wx.showLoading({ title: '登录中...' }) + + wx.login({ + success: (res) => { + if (res.code) { + API.user.wxSignin({ phoneNumber: mobile.trim(), name: name.trim(), code: res.code }) + .then((data) => { + if (data.code == 0) { + const app = getApp() + app.globalData.user = data.data.user + this.setData({ + isLoggedIn: true, + userInfo: app.globalData.user, + phoneNumber: this.maskPhoneNumber(app.globalData.user.profile.mobile || ''), + showLoginPopup: false, + loginForm: { name: '', mobile: '' } + }) + + wx.showToast({ title: '登录成功', icon: 'success' }) + } else { + wx.showToast({ title: '登录失败', icon: 'none' }) + } + + wx.hideLoading() + }) + } else { + wx.showToast({ title: '登录失败', icon: 'none' }) + } + } + }).catch(err => { + console.error('登录失败', err) + wx.showToast({ title: '登录失败,请重试', icon: 'none' }) + }).finally(() => { + wx.hideLoading() + }) + }, + + onMenuTap(e) { + const { index } = e.currentTarget.dataset + const item = this.data.menuList[index] + + if (!this.data.isLoggedIn) { + wx.showToast({ title: '请先登录', icon: 'none' }) + return + } + + if (item.url) { + wx.navigateTo({ url: item.url }) + } else { + wx.showToast({ title: '功能开发中', icon: 'none' }) + } + }, + + onLogout() { + wx.showModal({ + title: '退出登录', + content: '确定要退出登录吗?', + confirmColor: '#4c6ef5', + success: (res) => { + if (res.confirm) { + wx.showLoading({ title: '退出中...' }) + + API.user.signout().then(() => { + this.doLogout() + }).catch(() => { + this.doLogout() + }).finally(() => { + wx.hideLoading() + }) + } + } + }) + }, + + doLogout() { + const app = getApp() + app.globalData.user = null + wx.removeStorageSync('admin_user_info') + wx.removeStorageSync('admin_ai_session_id') + wx.removeStorageSync('admin_ai_chat_history') + + this.setData({ + isLoggedIn: false, + userInfo: null, + phoneNumber: '' + }) + + wx.showToast({ title: '已退出登录', icon: 'success' }) + }, + + onPopupContentTap() { + // 阻止冒泡,防止点击弹窗内容时关闭弹窗 + }, + + onClearCache() { + wx.showModal({ + title: '清除缓存', + content: '确定要清除所有缓存数据吗?', + confirmColor: '#4c6ef5', + success: (res) => { + if (res.confirm) { + wx.clearStorage({ + success: () => { + const app = getApp() + app.globalData.user = null + this.setData({ + isLoggedIn: false, + userInfo: null, + phoneNumber: '' + }) + wx.showToast({ title: '缓存已清除', icon: 'success' }) + } + }) + } + } + }) } -}) \ No newline at end of file +}) diff --git a/pages/set/index.json b/pages/set/index.json index 8835af0..274eda2 100644 --- a/pages/set/index.json +++ b/pages/set/index.json @@ -1,3 +1,9 @@ { - "usingComponents": {} -} \ No newline at end of file + "navigationBarTitleText": "设置", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black", + "backgroundColor": "#ffffff", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon" + } +} diff --git a/pages/set/index.wxml b/pages/set/index.wxml index 4d7e012..0fcf01c 100644 --- a/pages/set/index.wxml +++ b/pages/set/index.wxml @@ -1,2 +1,91 @@ - -pages/mine/index.wxml \ No newline at end of file + + + + + + + + + + + + + + {{item.title}} + + + + + + + + + + + + + + + 清除缓存 + + + + + + + + + + + + + + 版本 {{version}} + + diff --git a/pages/set/index.wxss b/pages/set/index.wxss index 9975426..db951ee 100644 --- a/pages/set/index.wxss +++ b/pages/set/index.wxss @@ -1 +1,303 @@ -/* pages/mine/index.wxss */ \ No newline at end of file +page { + background-color: #f5f6fa; + color: #1a1a2e; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; +} + +.set-page { + padding: 24rpx; +} + +.user-card { + background-color: #ffffff; + border-radius: 24rpx; + border: 1rpx solid #e5e7eb; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); + overflow: hidden; + margin-bottom: 24rpx; +} + +.user-info { + display: flex; + align-items: center; + padding: 40rpx 32rpx; + gap: 24rpx; +} + +.user-info.login-area { + align-items: center; +} + +.user-avatar { + width: 100rpx; + height: 100rpx; + background: linear-gradient(135deg, #4c6ef5, #748ffc); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + box-shadow: 0 4rpx 12rpx rgba(76, 110, 245, 0.25); +} + +.user-avatar.default-avatar { + background: #f3f4f6; + box-shadow: none; +} + +.avatar-text { + font-size: 36rpx; + font-weight: 600; + color: #ffffff; +} + +.user-detail { + flex: 1; + display: flex; + flex-direction: column; + gap: 8rpx; +} + +.user-name { + font-size: 32rpx; + font-weight: 600; + color: #1a1a2e; +} + +.user-phone { + font-size: 26rpx; + color: #9ca3af; +} + +.login-title { + font-size: 32rpx; + font-weight: 600; + color: #1a1a2e; +} + +.login-desc { + font-size: 24rpx; + color: #9ca3af; +} + +.logout-btn { + padding: 12rpx 28rpx; + background-color: rgba(255, 107, 107, 0.1); + border-radius: 28rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.logout-btn:active { + transform: scale(0.95); + opacity: 0.9; +} + +.logout-text { + font-size: 24rpx; + color: #ff6b6b; + font-weight: 500; +} + +.login-btn { + margin: 0; + padding: 16rpx 32rpx; + background: linear-gradient(135deg, #4c6ef5, #748ffc); + border-radius: 28rpx; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + border: none; +} + +.login-btn::after { + border: none; +} + +.login-btn:active { + transform: scale(0.95); + opacity: 0.9; +} + +.login-btn-text { + font-size: 26rpx; + color: #ffffff; + font-weight: 500; +} + +.menu-section { + background-color: #ffffff; + border-radius: 24rpx; + border: 1rpx solid #e5e7eb; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04); + overflow: hidden; + margin-bottom: 24rpx; +} + +.menu-list { + padding: 0 32rpx; +} + +.menu-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 32rpx 0; + border-bottom: 1rpx solid #f3f4f6; + transition: background-color 0.2s ease; +} + +.menu-item:last-child { + border-bottom: none; +} + +.menu-item:active { + background-color: rgba(76, 110, 245, 0.05); +} + +.menu-left { + display: flex; + align-items: center; + gap: 20rpx; +} + +.menu-title { + font-size: 28rpx; + color: #1a1a2e; + font-weight: 500; +} + +.menu-title.danger { + color: #ff6b6b; +} + +.menu-right { + display: flex; + align-items: center; +} + +.version-info { + display: flex; + align-items: center; + justify-content: center; + padding: 40rpx 0; +} + +.version-text { + font-size: 24rpx; + color: #9ca3af; +} + +/* 登录弹窗 */ +.login-popup { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.login-popup-content { + width: 80%; + max-width: 600rpx; + background-color: #ffffff; + border-radius: 24rpx; + overflow: hidden; + box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12); +} + +.popup-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 32rpx 32rpx 0; +} + +.popup-title { + font-size: 32rpx; + font-weight: 600; + color: #1a1a2e; +} + +.popup-close { + width: 56rpx; + height: 56rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; +} + +.popup-close:active { + background-color: #f3f4f6; +} + +.popup-body { + padding: 32rpx; +} + +.form-item { + margin-bottom: 24rpx; +} + +.form-item:last-child { + margin-bottom: 0; +} + +.form-label { + display: block; + font-size: 26rpx; + color: #6b7280; + margin-bottom: 12rpx; + font-weight: 500; +} + +.form-input { + width: 100%; + height: 80rpx; + background-color: #f5f6fa; + border-radius: 16rpx; + padding: 0 24rpx; + font-size: 28rpx; + color: #1a1a2e; + box-sizing: border-box; + border: 1rpx solid #e5e7eb; +} + +.form-input:focus { + border-color: #4c6ef5; +} + +.popup-footer { + padding: 0 32rpx 32rpx; +} + +.submit-btn { + width: 100%; + height: 88rpx; + background: linear-gradient(135deg, #4c6ef5, #748ffc); + border-radius: 44rpx; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4rpx 16rpx rgba(76, 110, 245, 0.25); + transition: all 0.2s ease; +} + +.submit-btn:active { + transform: scale(0.98); + opacity: 0.9; +} + +.submit-btn-text { + font-size: 30rpx; + color: #ffffff; + font-weight: 600; +} diff --git a/project.private.config.json b/project.private.config.json index 31c2e5e..3472505 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -3,7 +3,7 @@ "projectname": "wxapp_admin", "condition": {}, "setting": { - "urlCheck": true, + "urlCheck": false, "coverView": true, "lazyloadPlaceholderEnable": false, "skylineRenderEnable": false, diff --git a/utils/api.js b/utils/api.js index 191324e..7f3c282 100644 --- a/utils/api.js +++ b/utils/api.js @@ -6,6 +6,8 @@ const API = { wxSignin: (data) => request.post('/user/wxsignin', data), signout: (data) => request.post('/user/signout', data), update: (data) => request.post('/user/update', data), + userInfo: (data) => request.post('/user/userInfo', data), + userList: (data) => request.post('/user/list', data), }, escort: { @@ -15,6 +17,7 @@ const API = { createRecord: (data) => request.post('/health/escort-record', data), updateRecord: (id, data) => request.put(`/health/escort-record/${id}`, data), updateStatus: (id, data) => request.patch(`/health/escort-record/${id}/status`, data), + deleteRecord: (id) => request.delete(`/health/escort-record/${id}`), }, resource: { diff --git a/utils/request.js b/utils/request.js index 928dbd0..2cda7d5 100644 --- a/utils/request.js +++ b/utils/request.js @@ -12,7 +12,8 @@ class Request { const app = getApp() const token = app?.globalData?.user?.security?.token || '' - data.appId = 'wxapp-escort' + data.appId = 'wxapp-escort-admin' + data.token = token wx.request({ url: url.startsWith('http') ? url : `${this.baseURL}${url}`, @@ -45,6 +46,18 @@ class Request { post(url, data = {}, options = {}) { return this.request({ url, method: 'POST', data, ...options }) } + + put(url, data = {}, options = {}) { + return this.request({ url, method: 'PUT', data, ...options }) + } + + patch(url, data = {}, options = {}) { + return this.request({ url, method: 'PATCH', data, ...options }) + } + + delete(url, data = {}, options = {}) { + return this.request({ url, method: 'DELETE', data, ...options }) + } } const request = new Request()