\n"+r+""+s+">\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"}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}>\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='"+r+" ",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=` ",l}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:w(e.text)}},$=class{strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}},b=class e{options;renderer;textRenderer;constructor(e){this.options=e||T,this.options.renderer=this.options.renderer||new P,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new $}static parse(t,n){return new e(n).parse(t)}static parseInline(t,n){return new e(n).parseInline(t)}parse(e,t=!0){let n="";for(let r=0;r{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.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
+
+
+
+
+
+ {{userInfo.nickname ? userInfo.nickname[0] : '管'}}
+
+
+ {{userInfo.nickname || '管理员'}}
+ {{phoneNumber}}
+
+
+ 退出
+
+
+
+
+
+
+
+ 未登录
+ 登录后查看更多信息
+
+
+ 立即登录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 版本 {{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()