Browse Source

活动页面开发

dev
kola-web 15 hours ago
parent
commit
5748cc3468
  1. 11
      project.private.config.json
  2. 2
      src/api/request.ts
  3. 4
      src/app.json
  4. 22
      src/app.ts
  5. 22
      src/components/pickerArea/index.ts
  6. 65
      src/components/popup/index.scss
  7. 41
      src/components/popup/index.wxml
  8. 45
      src/custom-tab-bar/index.ts
  9. BIN
      src/images/badge3.png
  10. BIN
      src/images/bag/bag-decor.png
  11. BIN
      src/images/bg18.png
  12. BIN
      src/images/bg19.png
  13. BIN
      src/images/bg20.png
  14. BIN
      src/images/bg21.png
  15. BIN
      src/images/bg7.png
  16. 22
      src/images/chat/chat-bg.svg
  17. 9
      src/images/chat/chat-btn-bg.svg
  18. 4
      src/images/chat/chat-logo.svg
  19. BIN
      src/images/chat/chat-mascot.png
  20. 5
      src/images/chat/icon-what.svg
  21. 5
      src/images/chat/icon-who.svg
  22. BIN
      src/images/icon21-active.png
  23. BIN
      src/images/icon21.png
  24. BIN
      src/images/icon22-active.png
  25. BIN
      src/images/icon22.png
  26. BIN
      src/images/icon44.png
  27. BIN
      src/images/icon45.png
  28. BIN
      src/images/icon46.png
  29. BIN
      src/images/share-img1.png
  30. BIN
      src/images/title1.png
  31. BIN
      src/images/title2.png
  32. 19
      src/pages/articleDetail/index.wxml
  33. 9
      src/pages/bag/index.json
  34. 354
      src/pages/bag/index.scss
  35. 139
      src/pages/bag/index.ts
  36. 144
      src/pages/bag/index.wxml
  37. 7
      src/pages/chat/index.json
  38. 154
      src/pages/chat/index.scss
  39. 25
      src/pages/chat/index.ts
  40. 55
      src/pages/chat/index.wxml
  41. 21
      src/pages/confirmInfo/index.ts
  42. 4
      src/pages/confirmInfo/index.wxml
  43. 1
      src/pages/health/index.scss
  44. 2
      src/pages/health/index.wxml
  45. 1
      src/pages/heartDetail/index.scss
  46. 1
      src/pages/heartForm/index.scss
  47. 3
      src/pages/hotTopic/index.json
  48. 1
      src/pages/hotTopic/index.scss
  49. 44
      src/pages/hotTopic/index.ts
  50. 9
      src/pages/hotTopic/index.wxml
  51. 3
      src/pages/hotTopicDetail/index.json
  52. 25
      src/pages/hotTopicDetail/index.scss
  53. 98
      src/pages/hotTopicDetail/index.ts
  54. 75
      src/pages/hotTopicDetail/index.wxml
  55. 6
      src/pages/index/index.ts
  56. 8
      src/pages/index/index.wxml
  57. 1
      src/pages/login/index.scss
  58. 2
      src/pages/my/index.wxml
  59. 2
      src/pages/myComment/index.json
  60. 1
      src/pages/myComment/index.scss
  61. 102
      src/pages/myComment/index.ts
  62. 32
      src/pages/myComment/index.wxml
  63. 3
      src/pages/myWall/index.json
  64. 37
      src/pages/myWall/index.scss
  65. 109
      src/pages/myWall/index.ts
  66. 55
      src/pages/myWall/index.wxml
  67. 2
      src/pages/start/index.ts
  68. 1
      src/pages/userInfo/index.scss
  69. 28
      src/pages/userInfo/index.ts
  70. 4
      src/pages/userInfo/index.wxml
  71. 3
      src/pages/wall/index.json
  72. 24
      src/pages/wall/index.scss
  73. 150
      src/pages/wall/index.ts
  74. 84
      src/pages/wall/index.wxml
  75. 1
      src/pages/wallDetail/index.json
  76. 51
      src/pages/wallDetail/index.scss
  77. 274
      src/pages/wallDetail/index.ts
  78. 242
      src/pages/wallDetail/index.wxml
  79. 98
      src/pages/wallForm/index.scss
  80. 187
      src/pages/wallForm/index.ts
  81. 52
      src/pages/wallForm/index.wxml
  82. 76
      src/pages/wechatWork/index.scss
  83. 30
      src/pages/wechatWork/index.ts
  84. 19
      src/pages/wechatWork/index.wxml
  85. 3
      src/utils/tools.wxs
  86. 2
      typings/index.d.ts

11
project.private.config.json

@ -23,13 +23,20 @@
"miniprogram": { "miniprogram": {
"list": [ "list": [
{ {
"name": "修改手机号", "name": "随行包",
"pathName": "pages/changeTel/index", "pathName": "pages/bag/index",
"query": "", "query": "",
"scene": null, "scene": null,
"launchMode": "default" "launchMode": "default"
}, },
{ {
"name": "修改手机号",
"pathName": "pages/changeTel/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "隐私协议", "name": "隐私协议",
"pathName": "doc/pages/doc/index", "pathName": "doc/pages/doc/index",
"query": "", "query": "",

2
src/api/request.ts

@ -78,5 +78,5 @@ function errPicker(err) {
if (typeof err === 'string') { if (typeof err === 'string') {
return err return err
} }
return err.data || err.msg || err.errMsg || (err.detail && err.detail.errMsg) || '未知错误' return err.data || err.msg || err.message || err.errMsg || (err.detail && err.detail.errMsg) || '未知错误'
} }

4
src/app.json

@ -26,7 +26,9 @@
"pages/myComment/index", "pages/myComment/index",
"pages/myCollection/index", "pages/myCollection/index",
"pages/userInfo/index", "pages/userInfo/index",
"pages/changeTel/index" "pages/changeTel/index",
"pages/bag/index",
"pages/chat/index"
], ],
"subPackages": [ "subPackages": [
{ {

22
src/app.ts

@ -62,7 +62,6 @@ App<IAppOption>({
}, },
onShow(options) { onShow(options) {
console.log('DEBUGPRINT[316]: app.ts:63: options=', JSON.stringify(options)) console.log('DEBUGPRINT[316]: app.ts:63: options=', JSON.stringify(options))
// {"path":"pages/start/index","query":{"scene":"cid%3D3"},"scene":1047,"referrerInfo":{},"mode":"default","apiCategory":"default"}
let sceneData: any = {} let sceneData: any = {}
if (options.query.scene) { if (options.query.scene) {
sceneData = parseScene(options.query.scene) || {} sceneData = parseScene(options.query.scene) || {}
@ -74,21 +73,7 @@ App<IAppOption>({
} }
this.globalData.scene = sceneData this.globalData.scene = sceneData
this.startLogin(() => { this.startLogin()
// 已登录用户扫码,调用渠道码扫码记录接口
if (channelId) {
const { isLogin, isReg } = this.globalData.initLoginInfo
if (isLogin && isReg) {
wx.ajax({
method: 'POST',
url: '?r=psvt/channel/scan-channel',
data: { channelId: Number(channelId) },
loading: false,
showMsg: false,
})
}
}
})
}, },
startLogin(callback?: () => void) { startLogin(callback?: () => void) {
wx.login({ wx.login({
@ -115,9 +100,7 @@ App<IAppOption>({
return new Promise((resolve) => { return new Promise((resolve) => {
const checkLogin = () => { const checkLogin = () => {
if (this.globalData.loginState) { if (this.globalData.loginState) {
if (this.checkLoginType(type, false)) { resolve(this.checkLoginType(type, false))
resolve()
}
return return
} }
setTimeout(() => { setTimeout(() => {
@ -176,6 +159,7 @@ App<IAppOption>({
wx.reLaunch({ wx.reLaunch({
url: path, url: path,
}) })
return
}, },
mpBehavior(data: { PageName: string }) { mpBehavior(data: { PageName: string }) {
const url = '?r=wtx/mp-behavior/add' const url = '?r=wtx/mp-behavior/add'

22
src/components/pickerArea/index.ts

@ -257,28 +257,31 @@ Component({
}) })
}, },
handleShare() { handleShare() {
const { ProvinceName, ProvinceId, CityName, CityId } = this.data const { ProvinceName, ProvinceId, CityName, CityId, range } = this.data
if (!ProvinceId) { if (!ProvinceId) {
wx.showToast({ title: '请选择省份', icon: 'none' }) wx.showToast({ title: '请选择省份', icon: 'none' })
return return
} }
if (!CityId) { if (range && range.length && !CityId) {
wx.showToast({ title: '请选择城市', icon: 'none' }) wx.showToast({ title: '请选择城市', icon: 'none' })
return return
} }
this.setData({ this.setData({
show: false, show: false,
}) })
this.triggerEvent('change', [ const result: any = [
{ {
label: ProvinceName, label: ProvinceName,
value: ProvinceId, value: ProvinceId,
}, },
{ ]
if (CityId) {
result.push({
label: CityName, label: CityName,
value: CityId, value: CityId,
}, })
]) }
this.triggerEvent('change', result)
}, },
handleSelect(e) { handleSelect(e) {
const { id } = e.currentTarget.dataset const { id } = e.currentTarget.dataset
@ -290,7 +293,12 @@ Component({
getRangeList() { getRangeList() {
const { area, ProvinceId } = this.data const { area, ProvinceId } = this.data
if (!ProvinceId) return if (!ProvinceId) return
const range = area.filter((item: any) => item.value == ProvinceId)[0].children const province = area.filter((item: any) => item.value == ProvinceId)[0]
const range = province?.children || []
if (range.length === 0) {
this.setData({ range, active: 0 })
return
}
this.setData({ this.setData({
range, range,
active: 1, active: 1,

65
src/components/popup/index.scss

@ -117,6 +117,71 @@
} }
} }
.popup4 {
.popup-container {
.badge {
position: relative;
z-index: 1;
display: block;
margin: 0 auto;
width: 647rpx;
height: 340rpx;
}
.container {
margin-top: -126rpx;
width: 690rpx;
height: 850rpx;
border-radius: 42rpx;
.title {
padding-top: 158rpx;
text-align: center;
font-size: 48rpx;
color: rgba(30, 41, 59, 1);
font-weight: bold;
line-height: 56rpx;
}
.content {
margin-top: 16rpx;
font-size: 32rpx;
color: rgba(71, 85, 105, 1);
line-height: 48rpx;
text-align: center;
}
.code {
margin: 52rpx auto 0;
display: block;
width: 348rpx;
height: 348rpx;
}
}
}
}
.popup5 {
.popup-container {
.container {
padding-top: 572rpx;
width: 690rpx;
height: 720rpx;
border-radius: 42rpx;
box-sizing: border-box;
.btn {
display: block;
margin: 0 auto;
width: 626rpx;
height: 96rpx;
font-size: 40rpx;
color: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-radius: 24rpx 24rpx 24rpx 24rpx;
}
}
}
}
.close { .close {
margin: 32rpx auto 0; margin: 32rpx auto 0;
display: block; display: block;

41
src/components/popup/index.wxml

@ -11,7 +11,10 @@
> >
<!-- 心率提醒-引导关注公众号 --> <!-- 心率提醒-引导关注公众号 -->
<view class="popup1" wx:if="{{type==='popup1'}}"> <view class="popup1" wx:if="{{type==='popup1'}}">
<view class="popup-container" style="background: url('{{imageUrl}}bg13.png?t={{Timestamp}}') no-repeat top center/100%"> <view
class="popup-container"
style="background: url('{{imageUrl}}bg13.png?t={{Timestamp}}') no-repeat top center/100%"
>
<view class="title">关注“星动守护”</view> <view class="title">关注“星动守护”</view>
<view class="content">我们将在每周五下午7点提醒您记录</view> <view class="content">我们将在每周五下午7点提醒您记录</view>
<image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image> <image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image>
@ -23,7 +26,10 @@
<image class="badge" src="{{imageUrl}}badge1.png?t={{Timestamp}}"></image> <image class="badge" src="{{imageUrl}}badge1.png?t={{Timestamp}}"></image>
<view class="container" style="background: url('{{imageUrl}}bg15.png?t={{Timestamp}}') no-repeat top center/100%"> <view class="container" style="background: url('{{imageUrl}}bg15.png?t={{Timestamp}}') no-repeat top center/100%">
<view class="title">保存复诊记录成功!</view> <view class="title">保存复诊记录成功!</view>
<view class="code-wrap" style="background: url('{{imageUrl}}bg14.png?t={{Timestamp}}') no-repeat top center/100%"> <view
class="code-wrap"
style="background: url('{{imageUrl}}bg14.png?t={{Timestamp}}') no-repeat top center/100%"
>
<image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image> <image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image>
</view> </view>
<view class="content"> <view class="content">
@ -40,7 +46,10 @@
<image class="badge" src="{{imageUrl}}badge2.png?t={{Timestamp}}"></image> <image class="badge" src="{{imageUrl}}badge2.png?t={{Timestamp}}"></image>
<view class="container" style="background: url('{{imageUrl}}bg15.png?t={{Timestamp}}') no-repeat top center/100%"> <view class="container" style="background: url('{{imageUrl}}bg15.png?t={{Timestamp}}') no-repeat top center/100%">
<view class="title">保存复诊记录成功!</view> <view class="title">保存复诊记录成功!</view>
<view class="code-wrap" style="background: url('{{imageUrl}}bg14.png?t={{Timestamp}}') no-repeat top center/100%"> <view
class="code-wrap"
style="background: url('{{imageUrl}}bg14.png?t={{Timestamp}}') no-repeat top center/100%"
>
<image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image> <image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image>
</view> </view>
<view class="content"> <view class="content">
@ -51,6 +60,30 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 保持心率成功-引导关注公众号 -->
<view class="popup4" wx:if="{{type==='popup4'}}">
<view class="popup-container">
<image class="badge" src="{{imageUrl}}badge3.png?t={{Timestamp}}"></image>
<view class="container" style="background: url('{{imageUrl}}bg20.png?t={{Timestamp}}') no-repeat top center/100%">
<view class="title">安心随行大礼包申请成功!</view>
<view class="content">请您添加助手获得审批结果</view>
<image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image>
</view>
</view>
</view>
<!-- 首页随行包引导 -->
<view class="popup5" wx:if="{{type==='popup5'}}">
<view class="popup-container">
<view class="container" style="background: url('{{imageUrl}}bg21.png?t={{Timestamp}}') no-repeat top center/100%">
<view class="btn">免费领取></view>
</view>
</view>
</view>
<image wx:if="{{params.close}}" class="close" src="{{imageUrl}}clsoe.png?t={{Timestamp}}" bind:tap="handleCancel"></image> <image
wx:if="{{params.close}}"
class="close"
src="{{imageUrl}}clsoe.png?t={{Timestamp}}"
bind:tap="handleCancel"
></image>
</van-popup> </van-popup>

45
src/custom-tab-bar/index.ts

@ -23,7 +23,7 @@ Component({
iconActive: 'tabbar1-2-active', iconActive: 'tabbar1-2-active',
}, },
{ {
pagePath: '', pagePath: '/pages/chat/index',
custom: true, custom: true,
}, },
{ {
@ -45,21 +45,48 @@ Component({
ready() { ready() {
const pagePath = getCurrentPageUrl() const pagePath = getCurrentPageUrl()
const active = this.data.list.findIndex((item) => item.pagePath === pagePath) const active = this.data.list.findIndex((item) => item.pagePath === pagePath)
this.setData({ const wallConfig = app.globalData.wallConfig
active, if (wallConfig) {
anyWhere: app.globalData.anyWhere, const list: any[] = [...this.data.list]
}) const wallIndex = list.findIndex((item: any) => item.pagePath === '/pages/wall/index')
if (wallIndex > -1) {
list[wallIndex] = { ...list[wallIndex], text: wallConfig.navName || '交流墙' }
}
this.setData({ active, list, anyWhere: app.globalData.anyWhere })
} else {
this.setData({ active, anyWhere: app.globalData.anyWhere })
app.waitLogin?.({ type: 1 }).then(() => {
wx.ajax({
method: 'GET',
url: '?r=psvt/wall/get-config',
loading: false,
showMsg: false,
}).then((res: any) => {
const config = res || {}
app.globalData.wallConfig = config
this.updateWallName(config.navName || '交流墙')
})
})
}
}, },
}, },
methods: { methods: {
updateWallName(name: string) {
const list: any[] = [...this.data.list]
const wallIndex = list.findIndex((item: any) => item.pagePath === '/pages/wall/index')
if (wallIndex > -1) {
list[wallIndex] = { ...list[wallIndex], text: name }
this.setData({ list })
}
},
handleNav(e) { handleNav(e) {
const { index } = e.currentTarget.dataset const { index } = e.currentTarget.dataset
const { list } = this.data const { list } = this.data
const pagePath = list[index].pagePath const pagePath = list[index].pagePath
if (!pagePath) { const custom = list[index].custom
wx.showToast({ if (custom) {
title: '功能开发中,敬请期待', wx.navigateTo({
icon: 'none', url: '/pages/chat/index',
}) })
} }
wx.switchTab({ wx.switchTab({

BIN
src/images/badge3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
src/images/bag/bag-decor.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
src/images/bg18.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
src/images/bg19.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
src/images/bg20.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
src/images/bg21.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

BIN
src/images/bg7.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

After

Width:  |  Height:  |  Size: 792 KiB

22
src/images/chat/chat-bg.svg

@ -0,0 +1,22 @@
<svg width="730" height="554" viewBox="0 0 730 554" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.68">
<g opacity="0.28" filter="url(#filter0_f_48838_6242)">
<circle cx="267.447" cy="267.447" r="163.59" fill="#429EC5"/>
</g>
<g opacity="0.28" filter="url(#filter1_f_48838_6242)">
<circle cx="501.075" cy="324.978" r="125.069" fill="#EA581A" fill-opacity="0.32"/>
</g>
</g>
<defs>
<filter id="filter0_f_48838_6242" x="-0.000198473" y="-0.000198473" width="534.893" height="534.893" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="51.9" result="effect1_foregroundBlur_48838_6242"/>
</filter>
<filter id="filter1_f_48838_6242" x="272.149" y="96.0525" width="457.851" height="457.851" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="51.9" result="effect1_foregroundBlur_48838_6242"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

9
src/images/chat/chat-btn-bg.svg

@ -0,0 +1,9 @@
<svg width="152" height="49" viewBox="0 0 152 49" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M132 0C143.046 0 152 8.95431 152 20C152 30.5155 143.885 39.1342 133.575 39.9375L133.972 48.585L118.385 40H20C8.95431 40 0 31.0457 0 20C0 8.95431 8.95431 3.86555e-07 20 0H132Z" fill="url(#paint0_linear_48838_6303)"/>
<defs>
<linearGradient id="paint0_linear_48838_6303" x1="152" y1="24.2925" x2="1.25832e-06" y2="24.2925" gradientUnits="userSpaceOnUse">
<stop stop-color="#439FC7"/>
<stop offset="1" stop-color="#2F82A9"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 562 B

4
src/images/chat/chat-logo.svg

@ -0,0 +1,4 @@
<svg width="38" height="30" viewBox="0 0 38 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.7931 5.98187C20.8544 5.98187 21.9141 8.66806 24.9744 8.26452C28.4275 7.80799 30.5754 4.46146 34.7976 5.87181C39.0199 7.28216 38.6885 13.5268 35.9964 16.6003C34.6258 18.1614 32.0278 19.9142 26.795 19.8979H26.2263C25.4244 19.8979 24.7985 19.5066 24.5243 18.6587L24.1398 17.2198L22.3191 27.3328C22.0859 28.6535 20.1834 28.6616 19.9421 27.3409L18.3219 16.0948L17.5691 19.2172C17.3113 20.4481 16.0921 21.3979 14.6929 21.3979H10.5811C10.3929 21.3979 9.67285 21.1696 9.67285 20.5664C9.67285 19.7471 10.3929 19.6615 10.5811 19.6615H14.3451C15.3802 19.5147 15.6094 18.8829 15.8098 18.1696L17.3154 10.7673C17.7655 9.60557 19.4716 9.78492 19.6679 11.0159L21.3045 22.637L22.7855 14.6519C22.9615 13.4331 24.6634 13.266 25.0726 14.4277L26.0463 17.819C26.1036 18.0188 26.2877 18.1573 26.4964 18.1573H27.3187C33.6193 18.2266 35.3786 10.7999 32.842 8.96969C30.1826 7.04982 28.2188 9.89906 24.9089 9.25095C21.6768 8.61914 20.789 5.97779 20.789 5.97779L20.7931 5.98187Z" fill="#1C6287"/>
<path d="M10.7307 27.7068C11.5803 28.7292 10.4692 29.3824 9.734 28.7982C8.19409 27.581 6.71545 26.303 5.22456 24.5219C-2.85079 14.882 -0.293799 6.79602 4.37903 2.72665C8.42692 -0.799053 15.9631 -1.60238 19.9415 4.5605C21.3426 6.7311 22.5149 8.28095 24.3366 8.87331C26.0236 9.42103 28.3273 8.68262 28.3273 8.68262C23.438 10.3258 21.8123 8.71507 18.0095 6.3619C11.4414 2.29253 5.80457 6.90556 4.99582 12.3179C3.92155 19.5032 8.47593 24.9966 10.7307 27.7028V27.7068Z" fill="#EB5902"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/images/chat/chat-mascot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

5
src/images/chat/icon-what.svg

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.93427 2.93427C4.89045 0.978091 7.2457 0 10 0C12.7543 0 15.1095 0.978091 17.0657 2.93427C19.0219 4.89045 20 7.2457 20 10C20 12.7543 19.0219 15.1095 17.0657 17.0657C15.1095 19.0219 12.7543 20 10 20C7.2457 20 4.89045 19.0219 2.93427 17.0657C0.978091 15.1095 0 12.7543 0 10C0 7.2457 0.978091 4.89045 2.93427 2.93427Z" fill="#3C8CB1"/>
<path d="M10.0004 4.00635C8.90491 4.00635 7.96595 4.39758 7.18347 5.18006C6.401 5.96253 6.00977 6.9015 6.00977 7.99696H7.9816C7.9816 7.46488 8.18504 7.00322 8.59193 6.61198C8.99881 6.22075 9.46829 6.02513 10.0004 6.02513C10.5325 6.02513 11.0019 6.22075 11.4088 6.61198C11.8157 7.00322 12.0192 7.46488 12.0192 7.99696C12.0192 8.43514 11.8627 8.82638 11.5497 9.17067C11.2367 9.51496 10.8924 9.80447 10.5168 10.0392C10.1412 10.274 9.79693 10.6495 9.48394 11.166C9.17095 11.6824 9.01446 12.3006 9.01446 13.0204H10.9863C10.9863 12.3006 11.4871 11.4946 12.4886 10.6026C13.4902 9.71057 13.991 8.84203 13.991 7.99696C13.991 6.9015 13.5997 5.96253 12.8173 5.18006C12.0348 4.39758 11.0958 4.00635 10.0004 4.00635Z" fill="white"/>
<path d="M9.01446 14.0063V16.0251H10.9863V14.0063H9.01446Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

5
src/images/chat/icon-who.svg

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.93427 2.93427C4.89045 0.978091 7.2457 0 10 0C12.7543 0 15.1095 0.978091 17.0657 2.93427C19.0219 4.89045 20 7.2457 20 10C20 12.7543 19.0219 15.1095 17.0657 17.0657C15.1095 19.0219 12.7543 20 10 20C7.2457 20 4.89045 19.0219 2.93427 17.0657C0.978091 15.1095 0 12.7543 0 10C0 7.2457 0.978091 4.89045 2.93427 2.93427Z" fill="#3C8CB1"/>
<path d="M10.0004 4.00635C8.90491 4.00635 7.96595 4.39758 7.18347 5.18006C6.401 5.96253 6.00977 6.9015 6.00977 7.99696H7.9816C7.9816 7.46488 8.18504 7.00322 8.59193 6.61198C8.99881 6.22075 9.46829 6.02513 10.0004 6.02513C10.5325 6.02513 11.0019 6.22075 11.4088 6.61198C11.8157 7.00322 12.0192 7.46488 12.0192 7.99696C12.0192 8.43514 11.8627 8.82638 11.5497 9.17067C11.2367 9.51496 10.8924 9.80447 10.5168 10.0392C10.1412 10.274 9.79693 10.6495 9.48394 11.166C9.17095 11.6824 9.01446 12.3006 9.01446 13.0204H10.9863C10.9863 12.3006 11.4871 11.4946 12.4886 10.6026C13.4902 9.71057 13.991 8.84203 13.991 7.99696C13.991 6.9015 13.5997 5.96253 12.8173 5.18006C12.0348 4.39758 11.0958 4.00635 10.0004 4.00635Z" fill="white"/>
<path d="M9.01446 14.0063V16.0251H10.9863V14.0063H9.01446Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/images/icon21-active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

BIN
src/images/icon21.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/images/icon22-active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

BIN
src/images/icon22.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 B

After

Width:  |  Height:  |  Size: 910 B

BIN
src/images/icon44.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/images/icon45.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

BIN
src/images/icon46.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

BIN
src/images/share-img1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 783 KiB

BIN
src/images/title1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/images/title2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

19
src/pages/articleDetail/index.wxml

@ -19,7 +19,13 @@
<view class="title">相关推荐</view> <view class="title">相关推荐</view>
</view> </view>
<view class="r-list"> <view class="r-list">
<view class="list-card" wx:for="{{detail.Recommends}}" wx:key="Id" bind:tap="handleRecommendTap" data-id="{{item.Id}}"> <view
class="list-card"
wx:for="{{detail.Recommends}}"
wx:key="Id"
bind:tap="handleRecommendTap"
data-id="{{item.Id}}"
>
<view class="photo-wrap"> <view class="photo-wrap">
<image class="photo" mode="aspectFill" src="{{item.TitlePicLink}}"></image> <image class="photo" mode="aspectFill" src="{{item.TitlePicLink}}"></image>
<view class="type1" wx:if="{{item.Type==='1'}}">图文</view> <view class="type1" wx:if="{{item.Type==='1'}}">图文</view>
@ -51,12 +57,18 @@
<view class="op-wrap"> <view class="op-wrap">
<!-- 点赞 --> <!-- 点赞 -->
<view class="op" bind:tap="handleCompliment"> <view class="op" bind:tap="handleCompliment">
<image class="icon" src="{{detail.IsCompliment ? imageUrl + 'icon9-active.png?t=' + Timestamp : imageUrl + 'icon9.png?t=' + Timestamp}}"></image> <image
class="icon"
src="{{detail.IsCompliment ? imageUrl + 'icon9-active.png?t=' + Timestamp : imageUrl + 'icon9.png?t=' + Timestamp}}"
></image>
<view class="num {{detail.IsCompliment ? 'active1' : ''}}">{{detail.ComplimentNum || 0}}</view> <view class="num {{detail.IsCompliment ? 'active1' : ''}}">{{detail.ComplimentNum || 0}}</view>
</view> </view>
<!-- 收藏 --> <!-- 收藏 -->
<view class="op" bind:tap="handleCollect"> <view class="op" bind:tap="handleCollect">
<image class="icon" src="{{detail.IsCollect ? imageUrl + 'icon28-active.png?t=' + Timestamp : imageUrl + 'icon28.png?t=' + Timestamp}}"></image> <image
class="icon"
src="{{detail.IsCollect ? imageUrl + 'icon28-active.png?t=' + Timestamp : imageUrl + 'icon28.png?t=' + Timestamp}}"
></image>
<view class="num {{detail.IsCollect ? 'active2' : ''}}">{{detail.CollectionPeopleNum || 0}}</view> <view class="num {{detail.IsCollect ? 'active2' : ''}}">{{detail.CollectionPeopleNum || 0}}</view>
</view> </view>
<!-- 分享 --> <!-- 分享 -->
@ -69,6 +81,7 @@
</view> </view>
<view class="next" wx:if="{{next.Id}}" bind:tap="handleNext"> <view class="next" wx:if="{{next.Id}}" bind:tap="handleNext">
下一篇 下一篇
<van-icon name="arrow" />
</view> </view>
</view> </view>
</view> </view>

9
src/pages/bag/index.json

@ -0,0 +1,9 @@
{
"navigationStyle": "custom",
"usingComponents": {
"popup": "/components/popup/index",
"navbar": "/components/navbar/index",
"van-popup": "@vant/weapp/popup/index",
"van-icon": "@vant/weapp/icon/index"
}
}

354
src/pages/bag/index.scss

@ -0,0 +1,354 @@
page {
background-color: #f7fafc;
}
.page-title {
font-size: 36rpx;
color: rgba(51, 51, 51, 1);
padding-right: 100rpx;
}
.page {
padding-bottom: 260rpx;
.hero {
padding: 0 40rpx;
display: flex;
align-items: flex-start;
justify-content: space-between;
.hero-text {
padding-top: 78rpx;
.hero-title1 {
display: block;
width: 233rpx;
}
.hero-title2 {
margin-top: 20rpx;
display: block;
width: 353rpx;
height: 56rpx;
}
.hero-sub {
margin-top: 24rpx;
font-size: 28rpx;
font-weight: 400;
color: #64748b;
line-height: 40rpx;
}
}
.hero-decor {
margin-top: 48rpx;
width: 356rpx;
height: 356rpx;
flex-shrink: 0;
}
}
.card {
margin: 30rpx;
padding: 0;
border-radius: 32rpx;
border: 2rpx solid #fff;
background: rgba(255, 255, 255, 0.7);
box-shadow: 0 8rpx 64rpx 0 rgba(0, 99, 132, 0.08);
backdrop-filter: blur(10rpx);
overflow: hidden;
}
.step {
display: flex;
align-items: center;
gap: 24rpx;
padding: 24rpx 32rpx;
background: rgba(47, 130, 169, 0.08);
border: 2rpx solid #fff;
border-radius: 24rpx;
margin: 32rpx 32rpx 0;
.step-badge {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 29rpx;
font-weight: 700;
line-height: 44rpx;
flex-shrink: 0;
}
.step-title {
font-size: 36rpx;
font-weight: 500;
color: #2f82a9;
line-height: 44rpx;
}
}
.card-body {
padding: 48rpx 32rpx 54rpx;
.card-desc {
padding-left: 12rpx;
width: 400rpx;
font-size: 28rpx;
color: #64748b;
line-height: 38rpx;
padding-right: 20rpx;
white-space: pre-line;
}
.upload-grid {
margin-top: 53rpx;
}
.rejected {
margin-top: 24rpx;
padding: 16rpx 24rpx;
border-radius: 16rpx;
background: rgba(186, 26, 26, 0.08);
box-sizing: border-box;
font-size: 28rpx;
color: rgba(186, 26, 26, 1);
line-height: 40rpx;
display: flex;
.icon {
margin-top: 4rpx;
display: inline-block;
flex-shrink: 0;
margin-right: 16rpx;
width: 32rpx;
height: 32rpx;
vertical-align: -4rpx;
}
.blod {
display: inline-block;
font-weight: bold;
}
}
}
.upload-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 24rpx;
.upload-item {
aspect-ratio: 1 / 1;
.upload-empty {
width: 100%;
height: 100%;
border-radius: 24rpx;
background: #f7fafc;
border: 2rpx dashed rgba(148, 163, 184, 0.26);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16rpx;
box-sizing: border-box;
.upload-icon {
width: 56rpx;
height: 56rpx;
}
.upload-placeholder {
font-size: 28rpx;
color: rgba(148, 163, 184, 0.51);
}
}
.upload {
position: relative;
width: 100%;
height: 100%;
border-radius: 24rpx;
overflow: hidden;
.u-img {
display: block;
width: 100%;
height: 100%;
}
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.29);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.icon {
width: 44rpx;
height: 44rpx;
}
.status {
margin-top: 12rpx;
font-size: 28rpx;
color: rgba(255, 255, 255, 1);
}
}
.del {
position: absolute;
right: 0;
top: 0;
width: 40rpx;
height: 40rpx;
z-index: 9;
background-color: rgba(4, 4, 4, 0.31);
border-radius: 0 16rpx;
color: #fff;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
.card-body2 {
padding: 36rpx 32rpx 32rpx;
.c-item {
display: flex;
gap: 20rpx;
&.c-item2 {
margin-top: 50rpx;
}
.aside {
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
padding-bottom: 23rpx;
.order {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
font-size: 24rpx;
color: rgba(255, 255, 255, 1);
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
display: flex;
align-items: center;
justify-content: center;
}
.line {
flex: 1;
border-right: 1px dashed rgba(47, 130, 169, 0.38);
}
}
.container {
flex: 1;
.title {
font-size: 32rpx;
color: rgba(30, 41, 59, 1);
font-weight: bold;
}
.content {
width: 338rpx;
margin-top: 12rpx;
font-size: 28rpx;
line-height: 40rpx;
color: rgba(100, 116, 139, 1);
.high {
display: inline;
color: rgba(47, 130, 169, 1);
}
}
.share {
margin-top: 24rpx;
display: flex;
align-items: center;
gap: 34rpx;
.share-img {
width: 212rpx;
height: 318rpx;
border-radius: 32rpx;
}
.btn {
padding: 8rpx 16rpx;
font-size: 32rpx;
color: rgba(47, 130, 169, 1);
line-height: 48rpx;
border-radius: 12rpx;
border: 1px solid rgba(47, 130, 169, 0.16);
box-sizing: border-box;
}
}
.upload-grid {
margin-top: 24rpx;
gap: 22rpx;
}
}
}
}
.consent {
margin: 32rpx 30rpx;
font-size: 26rpx;
color: rgba(148, 163, 184, 1);
line-height: 40rpx;
.check-content {
padding-top: 4rpx;
display: inline-block;
}
.wx-checkbox-input {
width: 32rpx;
height: 32rpx;
border-radius: 8rpx;
&.wx-checkbox-input-checked {
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-color: transparent;
&::before {
font-size: 28rpx;
color: #fff;
}
}
}
}
.footer {
display: flex;
gap: 24rpx;
padding: 24rpx 30rpx calc(env(safe-area-inset-bottom) + 24rpx);
box-sizing: border-box;
.btn {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24rpx 0;
border-radius: 24rpx;
.btn-title {
font-size: 32rpx;
font-weight: 700;
color: #fff;
line-height: 44rpx;
}
.btn-sub {
font-size: 28rpx;
font-weight: 400;
color: #fff;
line-height: 40rpx;
}
}
.btn-disabled {
background: rgba(148, 163, 184, 0.5);
}
.btn-primary {
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
}
}
}

139
src/pages/bag/index.ts

@ -0,0 +1,139 @@
type UploadStatus = 'reviewing' | 'approved' | 'rejected'
interface UploadItem {
url: string
status: UploadStatus
rejectReason?: string
}
const app = getApp<IAppOption>()
Page({
data: {
popupShow: false,
popupType: 'popup4',
popupParams: {
position: 'center',
close: true,
} as any,
id: '',
detail: {} as any,
// 上传列表(已上传项),null 表示空位待上传
step1Slots: [null, null, null] as (UploadItem | null)[],
step2Slots: [null, null, null] as (UploadItem | null)[],
agreed: false,
},
onLoad() {},
onShow() {},
handleBack() {
wx.navigateBack()
},
// 选择并上传图片
handleChooseMedia(e: any) {
const { step, index } = e.currentTarget.dataset
const key = step === 1 ? 'step1Slots' : 'step2Slots'
const slots = [...this.data[key]]
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['original', 'compressed'],
success: (res) => {
const tempFile = res.tempFiles[0]
this.uploadImage(tempFile.tempFilePath, key, index, slots)
},
})
},
// 上传图片到服务器
uploadImage(filePath: string, key: string, index: number, slots: (UploadItem | null)[]) {
wx.showLoading({ title: '正在上传' })
const url = `${app.globalData.upFileUrl}?r=file-service/upload-img`
wx.uploadFile({
filePath,
name: 'file',
url,
success: (res) => {
const data = JSON.parse(res.data)
const imgUrl = data.data.Url
// 新上传默认为审核中状态
slots[index] = { url: imgUrl, status: 'reviewing' }
this.setData({ [key]: [...slots] })
},
fail: () => {
wx.showToast({ title: '上传失败', icon: 'error' })
},
complete: () => {
wx.hideLoading()
},
})
},
// 预览图片
handlePreviewImage(e: any) {
const { step, index } = e.currentTarget.dataset
const key = step === 1 ? 'step1Slots' : 'step2Slots'
const slots = this.data[key] as (UploadItem | null)[]
const urls = slots.filter((item): item is UploadItem => !!item).map((item) => item.url)
wx.previewImage({ current: urls[index] || urls[0], urls })
},
// 删除上传项(仅审核中、驳回可删)
handleDeleteUpload(e: any) {
const { step, index } = e.currentTarget.dataset
const key = step === 1 ? 'step1Slots' : 'step2Slots'
const slots = [...this.data[key]]
slots[index] = null
this.setData({ [key]: [...slots] })
},
toggleAgree() {
this.setData({ agreed: !this.data.agreed })
},
handleDownloadPoster() {
wx.showToast({ title: '海报下载开发中', icon: 'none' })
},
// 获取已上传(非驳回)的图片 URL 列表
getValidImages(slots: (UploadItem | null)[]): string[] {
return slots
.filter((item): item is UploadItem => !!item && item.status !== 'rejected')
.map((item) => item.url)
},
handleStep1Submit() {
const { step1Slots, agreed } = this.data
const validImages = this.getValidImages(step1Slots as (UploadItem | null)[])
if (!validImages.length) {
wx.showToast({ title: '请先上传病例', icon: 'none' })
return
}
if (!agreed) {
wx.showToast({ title: '请先勾选确认', icon: 'none' })
return
}
wx.showToast({ title: '提交开发中', icon: 'none' })
},
handleAllSubmit() {
const { step1Slots, step2Slots, agreed } = this.data
const valid1 = this.getValidImages(step1Slots as (UploadItem | null)[])
const valid2 = this.getValidImages(step2Slots as (UploadItem | null)[])
if (!valid1.length || !valid2.length) {
wx.showToast({ title: '请完成全部任务', icon: 'none' })
return
}
if (!agreed) {
wx.showToast({ title: '请先勾选确认', icon: 'none' })
return
}
wx.showToast({ title: '提交开发中', icon: 'none' })
},
})
export {}

144
src/pages/bag/index.wxml

@ -0,0 +1,144 @@
<navbar fixed custom-style="background:{{background}}" back>
<view class="page-title" slot="left" bind:tap="handleBack">
<van-icon name="arrow-left" />
</view>
</navbar>
<view
class="page"
style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;"
>
<view class="hero">
<view class="hero-text">
<image class="hero-title1" mode="widthFix" src="{{imageUrl}}title1.png?t={{Timestamp}}"></image>
<image class="hero-title2" mode="widthFix" src="{{imageUrl}}title2.png?t={{Timestamp}}"></image>
<view class="hero-sub">
上传病例并完成任务,领取
<view></view>
安心随行礼包
</view>
</view>
<image class="hero-decor" src="{{imageUrl}}bg19.png?t={{Timestamp}}" mode="aspectFill"></image>
</view>
<view class="card" style="background: url('{{imageUrl}}bg18.png?t={{Timestamp}}') no-repeat top 100rpx right -53rpx/385rpx 355rpx">
<view class="step">
<view class="step-badge">1</view>
<view class="step-title">上传病例,获得随行小礼</view>
</view>
<view class="card-body">
<view class="card-desc">上传您的病例即可获得专属随行小礼哦~病历,诊断证明,心电图照片 任意一项</view>
<view class="upload-grid">
<view class="upload-item" wx:for="{{step1Slots}}" wx:key="index" data-step="1" data-index="{{index}}">
<view class="upload-empty" bind:tap="handleChooseMedia" data-step="1" data-index="{{index}}" wx:if="{{item}}">
<image class="upload-icon" src="{{imageUrl}}icon44.png?t={{Timestamp}}" mode="aspectFit"></image>
<view class="upload-placeholder">待上传</view>
</view>
<view class="upload">
<image class="u-img" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image>
<view class="mask">
<image class="icon" src="{{imageUrl}}icon45.png?t={{Timestamp}}"></image>
<view class="status">审核中</view>
<!-- <image class="icon" src="{{imageUrl}}icon45.png?t={{Timestamp}}"></image> -->
<!-- <view class="status">驳回</view> -->
<!-- <image class="icon" src="{{imageUrl}}icon46.png?t={{Timestamp}}"></image> -->
<!-- <view class="status">审核通过</view> -->
</view>
<view class="del">
<van-icon name="cross" />
</view>
</view>
</view>
</view>
<view class="rejected">
<image class="icon" src="{{imageUrl}}icon39.png?t={{Timestamp}}"></image>
<view>
<view class="blod">驳回原因:</view>
驳回原因:清上传病例、诊断证明心点图照片确诊信息
</view>
</view>
</view>
</view>
<view class="card">
<view class="step">
<view class="step-badge">2</view>
<view class="step-title">分享星动守护 解锁安心随行礼</view>
</view>
<view class="card-body2" style="background: url('{{imageUrl}}bg19.png?t={{Timestamp}}') no-repeat top 0 right 8rpx/252rpx 234rpx">
<view class="c-item">
<view class="aside">
<view class="order">1</view>
<view class="line"></view>
</view>
<view class="container">
<view class="title">发布到小红书</view>
<view class="content">
推荐星动守护平台分享到小红书,带上话题:
<view class="high">#星动守护PSVT小助手</view>
</view>
<view class="share">
<image class="share-img" mode="aspectFill" src="{{imageUrl}}share-img1.png?t={{Timestamp}}"></image>
<view class="btn">一键下载</view>
</view>
</view>
</view>
<view class="c-item c-item2">
<view class="aside">
<view class="order">2</view>
</view>
<view class="container">
<view class="title">上传发布截图</view>
<view class="content">用于确认任务是否完成</view>
<view class="upload-grid">
<view class="upload-item" wx:for="{{step1Slots}}" wx:key="index" data-step="1" data-index="{{index}}">
<view
class="upload-empty"
bind:tap="handleChooseMedia"
data-step="1"
data-index="{{index}}"
wx:if="{{!item}}"
>
<image class="upload-icon" src="{{imageUrl}}icon44.png?t={{Timestamp}}" mode="aspectFit"></image>
<view class="upload-placeholder">待上传</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="consent">
<checkbox class="check">
<view class="check-content">我已确认上传内容真实有效,并同意用于活动资格审核。</view>
</checkbox>
</view>
<view class="footer">
<block wx:if="{{true}}">
<view class="btn btn-disabled" bind:tap="handleStep1Submit">
<view class="btn-title">完成第1步任务</view>
<view class="btn-sub">领取随心小礼></view>
</view>
<view class="btn btn-primary" bind:tap="handleAllSubmit">
<view class="btn-title">完成全部任务</view>
<view class="btn-sub">领取随行大礼包></view>
</view>
</block>
<view class="btn btn-primary" wx:elif="{{false}}">
<view class="btn-title">添加客服,获得审批结果></view>
</view>
<view class="btn btn-primary" wx:elif="{{false}}">
<view class="btn-title">重新提交</view>
</view>
</view>
</view>
<popup
show="{{popupShow}}"
type="{{popupType}}"
params="{{popupParams}}"
bind:ok="handlePopupOk"
bind:cancel="handlePopupCancel"
></popup>

7
src/pages/chat/index.json

@ -0,0 +1,7 @@
{
"navigationStyle": "custom",
"usingComponents": {
"navbar": "/components/navbar/index",
"van-icon": "@vant/weapp/icon/index"
}
}

154
src/pages/chat/index.scss

@ -0,0 +1,154 @@
page {
background-color: #f7fafc;
}
.page-title {
font-size: 36rpx;
color: rgba(51, 51, 51, 1);
padding-right: 100rpx;
}
.page {
position: relative;
min-height: 100vh;
padding-bottom: 60rpx;
}
.bg-pattern {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 692rpx;
opacity: 0.68;
pointer-events: none;
}
// 吉祥物区域
.mascot-wrap {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 32rpx;
position: relative;
}
.greeting-bubble {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 304rpx;
height: 98rpx;
.greeting-bg {
position: absolute;
width: 100%;
height: 100%;
}
.greeting-text {
position: relative;
z-index: 1;
font-size: 32rpx;
font-weight: 700;
color: #fff;
line-height: 48rpx;
}
}
.mascot {
width: 302rpx;
height: 384rpx;
margin-top: 12rpx;
}
// 信息卡片
.info-card {
margin: 24rpx 30rpx 0;
padding: 32rpx;
background: #fff;
border-radius: 32rpx;
}
.card-header {
display: flex;
align-items: center;
gap: 14rpx;
margin-bottom: 24rpx;
.card-icon {
width: 40rpx;
height: 40rpx;
flex-shrink: 0;
}
.card-title {
font-size: 32rpx;
font-weight: 700;
color: #2f82a9;
line-height: 48rpx;
}
}
.card-desc {
font-size: 28rpx;
font-weight: 400;
color: #475569;
line-height: 48rpx;
}
// 升级按钮
.upgrade-btn {
margin: 48rpx auto 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 434rpx;
padding: 16rpx 0;
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-radius: 32rpx;
.upgrade-line1 {
font-size: 28rpx;
font-weight: 700;
color: #fff;
line-height: 42rpx;
}
.upgrade-line2 {
font-size: 28rpx;
font-weight: 400;
color: #fff;
line-height: 42rpx;
}
}
// 通知勾选
.notify {
margin: 32rpx 30rpx;
font-size: 26rpx;
color: rgba(148, 163, 184, 1);
line-height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
.check-content {
padding-top: 4rpx;
display: inline-block;
}
.wx-checkbox-input {
width: 32rpx;
height: 32rpx;
border-radius: 8rpx;
&.wx-checkbox-input-checked {
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-color: transparent;
&::before {
font-size: 28rpx;
color: #fff;
}
}
}
}

25
src/pages/chat/index.ts

@ -0,0 +1,25 @@
Page({
data: {
notifyChecked: false,
},
onLoad() {},
onShow() {},
handleBack() {
wx.navigateBack()
},
toggleNotify() {
const notifyChecked = !this.data.notifyChecked
this.setData({ notifyChecked })
if (notifyChecked) {
wx.showToast({ title: '已开启上线通知', icon: 'none' })
}
},
handleNotify() {
if (!this.data.notifyChecked) {
this.setData({ notifyChecked: true })
wx.showToast({ title: '已开启上线通知', icon: 'none' })
}
},
})
export {}

55
src/pages/chat/index.wxml

@ -0,0 +1,55 @@
<navbar fixed custom-style="background:{{background}}" back>
<view class="page-title" slot="left" bind:tap="handleBack">
<van-icon name="arrow-left" />
</view>
</navbar>
<view
class="page"
style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;"
>
<!-- 背景装饰 -->
<image class="bg-pattern" src="{{imageUrl}}chat/chat-bg.svg?t={{Timestamp}}" mode="aspectFill"></image>
<!-- 吉祥物 + 问候气泡 -->
<view class="mascot-wrap">
<view class="greeting-bubble">
<image class="greeting-bg" src="{{imageUrl}}chat/chat-btn-bg.svg?t={{Timestamp}}" mode="aspectFill"></image>
<text class="greeting-text">Hello,我是小豚</text>
</view>
<image class="mascot" src="{{imageUrl}}chat/chat-mascot.png?t={{Timestamp}}" mode="aspectFill"></image>
</view>
<!-- 卡片:我是谁 -->
<view class="info-card">
<view class="card-header">
<image class="card-icon" src="{{imageUrl}}chat/icon-who.svg?t={{Timestamp}}" mode="aspectFit"></image>
<text class="card-title">我是谁</text>
</view>
<view class="card-desc">hello,我是你的智能助手小豚,为你提供阵发性 室上速健康科普与慢病管理服务。</view>
</view>
<!-- 卡片:我能干嘛 -->
<view class="info-card">
<view class="card-header">
<image class="card-icon" src="{{imageUrl}}chat/icon-what.svg?t={{Timestamp}}" mode="aspectFit"></image>
<text class="card-title">我能干嘛</text>
</view>
<view class="card-desc">
作为阵发性室上速(PSVT)智能助手,我将为你 提供解答疾病问题、指导急性发作处理、提醒定
期复诊等全周期健康管理服务,你有任何关于 PSVT的问题都可随时咨询我。往后日常生活里,
我将长久相伴,全程守护你的健康。
</view>
</view>
<!-- 升级中按钮 -->
<view class="upgrade-btn" bind:tap="handleNotify">
<view class="upgrade-line1">我在努力学习升级中,</view>
<view class="upgrade-line2">很快就和您见面啦!</view>
</view>
<!-- 上线通知勾选 -->
<view class="notify" bind:tap="toggleNotify">
<checkbox class="check">上线时通知我</checkbox>
</view>
</view>

21
src/pages/confirmInfo/index.ts

@ -39,7 +39,9 @@ Page({
menuTop: menuRes.top, menuTop: menuRes.top,
currentMonth, currentMonth,
}) })
this.getBaseData() app.waitLogin({ type: 'any' }).then(() => {
this.getBaseData()
})
}, },
getBaseData() { getBaseData() {
wx.ajax({ wx.ajax({
@ -77,12 +79,14 @@ Page({
}, },
handleAreaChange(e: any) { handleAreaChange(e: any) {
const [province, city] = e.detail const [province, city] = e.detail
const cityId = city?.value || ''
const cityName = city?.label || ''
this.setData({ this.setData({
provinceId: province.value, provinceId: province.value,
provinceName: province.label, provinceName: province.label,
cityId: city.value, cityId,
cityName: city.label, cityName,
regionText: [province.label, city.label].filter(Boolean).join(' '), regionText: [province.label, cityName].filter(Boolean).join(' '),
}) })
}, },
handleDiagnosisTimeChange(e: WechatMiniprogram.PickerChange) { handleDiagnosisTimeChange(e: WechatMiniprogram.PickerChange) {
@ -106,12 +110,16 @@ Page({
this.setData({ step: 1 }) this.setData({ step: 1 })
}, },
handleSubmit() { handleSubmit() {
const { diagnosisStatus, surgeryStatus } = this.data const { diagnosisStatus, diagnosisTime, surgeryStatus } = this.data
if (!diagnosisStatus) { if (!diagnosisStatus) {
wx.showToast({ title: '请选择确诊状态', icon: 'none' }) wx.showToast({ title: '请选择确诊状态', icon: 'none' })
return return
} }
if (!surgeryStatus) { if (diagnosisStatus === 2 && !diagnosisTime) {
wx.showToast({ title: '请选择确诊时间', icon: 'none' })
return
}
if (diagnosisStatus === 2 && !surgeryStatus) {
wx.showToast({ title: '请选择手术状态', icon: 'none' }) wx.showToast({ title: '请选择手术状态', icon: 'none' })
return return
} }
@ -124,7 +132,6 @@ Page({
cityId, cityId,
cityName, cityName,
diagnosisStatus: ds, diagnosisStatus: ds,
diagnosisTime,
surgeryStatus: ss, surgeryStatus: ss,
} = this.data } = this.data
const channelId = app.globalData.scene?.channelId || '' const channelId = app.globalData.scene?.channelId || ''

4
src/pages/confirmInfo/index.wxml

@ -17,7 +17,7 @@
</view> </view>
<view class="tip"> <view class="tip">
<image class="icon" src="{{imageUrl}}icon4.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon4.png?t={{Timestamp}}"></image>
<view class="content">这些信息将用于优化内容推荐与服务提醒,不作为诊断或 治疗建议依据。</view> <view class="content">这些信息将用于优化内容推荐与服务提醒,不作为诊断或治疗建议依据。</view>
</view> </view>
<!-- Step 1: 基础信息 --> <!-- Step 1: 基础信息 -->
@ -99,7 +99,7 @@
</picker> </picker>
</view> </view>
</view> </view>
<view class="row"> <view class="row" wx:if="{{diagnosisStatus===2}}">
<view class="row-title">是否接受过相关手术治疗</view> <view class="row-title">是否接受过相关手术治疗</view>
<view class="select4"> <view class="select4">
<view <view

1
src/pages/health/index.scss

@ -84,6 +84,7 @@ page {
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%); background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-color: transparent; border-color: transparent;
&::before { &::before {
font-size: 28rpx;
color: #fff; color: #fff;
} }
} }

2
src/pages/health/index.wxml

@ -139,7 +139,7 @@
<image class="p-img" mode="aspectFill" src="{{img}}"></image> <image class="p-img" mode="aspectFill" src="{{img}}"></image>
</view> </view>
</view> </view>
<view class="title">{{item.isUpdatePrescription ? '已更新处方' : '未更新处方'}}{{isUpdateSurgery ? '已更新手术诊断' : '未手术诊断'}}</view> <view class="title">{{item.isUpdatePrescription ? '已更新处方' : '未更新处方'}};{{isUpdateSurgery ? '已更新手术诊断' : '未手术诊断'}}</view>
<view class="content" wx:if="{{item.remark}}">{{item.remark}}</view> <view class="content" wx:if="{{item.remark}}">{{item.remark}}</view>
</view> </view>
</view> </view>

1
src/pages/heartDetail/index.scss

@ -119,6 +119,7 @@ page {
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%); background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-color: transparent; border-color: transparent;
&::before { &::before {
font-size: 28rpx;
color: #fff; color: #fff;
} }
} }

1
src/pages/heartForm/index.scss

@ -123,6 +123,7 @@ page {
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%); background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-color: transparent; border-color: transparent;
&::before { &::before {
font-size: 28rpx;
color: #fff; color: #fff;
} }
} }

3
src/pages/hotTopic/index.json

@ -2,6 +2,7 @@
"navigationStyle": "custom", "navigationStyle": "custom",
"usingComponents": { "usingComponents": {
"popup": "/components/popup/index", "popup": "/components/popup/index",
"navbar": "/components/navbar/index" "navbar": "/components/navbar/index",
"pagination": "/components/pagination/index"
} }
} }

1
src/pages/hotTopic/index.scss

@ -5,6 +5,7 @@ page {
.page-title { .page-title {
font-size: 36rpx; font-size: 36rpx;
color: #fff; color: #fff;
padding-right: 100rpx;
} }
.page { .page {

44
src/pages/hotTopic/index.ts

@ -7,10 +7,51 @@ Page({
popupParams: { popupParams: {
position: 'bottom', position: 'bottom',
} as any, } as any,
topicList: [] as Array<any>,
topicPagination: {
page: 1,
pages: 1,
count: 0,
},
}, },
onLoad() { onLoad() {
app.waitLogin({ type: 1 }).then(() => {}) app.waitLogin({ type: 1 }).then(() => {})
}, },
onShow() {
this.getTopicList()
},
getTopicList(page = 1, append = false) {
wx.ajax({
method: 'GET',
url: '?r=psvt/topic/list',
data: {
page,
count: 10,
},
}).then((res: any) => {
const list = res.list || []
this.setData({
topicList: append ? this.data.topicList.concat(list) : list,
topicPagination: {
page: res.page || 1,
pages: res.pages || 1,
count: Number(res.count) || 0,
},
})
})
},
onReachBottom() {
const { topicPagination } = this.data
if (topicPagination.page < topicPagination.pages) {
this.getTopicList(topicPagination.page + 1, true)
}
},
handleTopicTap(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/hotTopicDetail/index?id=${id}`,
})
},
handlePopupOk() { handlePopupOk() {
const { popupType } = this.data const { popupType } = this.data
if (popupType === 'argument') { if (popupType === 'argument') {
@ -74,6 +115,9 @@ Page({
url: '/pages/wechatWork/index', url: '/pages/wechatWork/index',
}) })
}, },
handleBack() {
wx.navigateBack()
},
}) })
export {} export {}

9
src/pages/hotTopic/index.wxml

@ -1,5 +1,5 @@
<navbar fixed custom-style="background:{{background}}" back> <navbar fixed custom-style="background:{{background}}" back>
<view class="page-title" slot="left"> <view class="page-title" slot="left" bind:tap="handleBack">
<van-icon name="arrow-left" /> <van-icon name="arrow-left" />
</view> </view>
</navbar> </navbar>
@ -10,11 +10,12 @@
> >
<view class="container"> <view class="container">
<view class="tp-list"> <view class="tp-list">
<view class="li" wx:for="{{10}}" wx:key="index"> <view class="li" wx:for="{{topicList}}" wx:key="id" bind:tap="handleTopicTap" data-id="{{item.id}}">
<view class="num">{{index+1}}</view> <view class="num">{{index+1}}</view>
<view class="content"># 我的心悸经历</view> <view class="content"># {{item.topicName}}</view>
<view class="total">230篇帖子</view> <view class="total">{{item.postCount}}篇帖子</view>
</view> </view>
</view> </view>
<pagination pagination="{{topicPagination}}" />
</view> </view>
</view> </view>

3
src/pages/hotTopicDetail/index.json

@ -2,6 +2,7 @@
"navigationStyle": "custom", "navigationStyle": "custom",
"usingComponents": { "usingComponents": {
"popup": "/components/popup/index", "popup": "/components/popup/index",
"navbar": "/components/navbar/index" "navbar": "/components/navbar/index",
"pagination": "/components/pagination/index"
} }
} }

25
src/pages/hotTopicDetail/index.scss

@ -5,6 +5,7 @@ page {
.page-title { .page-title {
font-size: 36rpx; font-size: 36rpx;
color: #fff; color: #fff;
padding-right: 100rpx;
} }
.page { .page {
@ -143,6 +144,24 @@ page {
height: 352rpx; height: 352rpx;
border-radius: 24rpx; border-radius: 24rpx;
} }
.video-cover {
position: relative;
overflow: hidden;
.p-img {
width: 100%;
height: 100%;
border-radius: 24rpx;
}
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80rpx;
height: 80rpx;
z-index: 1;
}
.photo9 { .photo9 {
margin-top: 24rpx; margin-top: 24rpx;
display: grid; display: grid;
@ -151,11 +170,17 @@ page {
.p-item { .p-item {
width: 100%; width: 100%;
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
position: relative;
overflow: hidden;
.p-img { .p-img {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 24rpx; border-radius: 24rpx;
} }
.play-icon {
width: 56rpx;
height: 56rpx;
}
} }
} }
.comment { .comment {

98
src/pages/hotTopicDetail/index.ts

@ -7,9 +7,100 @@ Page({
popupParams: { popupParams: {
position: 'bottom', position: 'bottom',
} as any, } as any,
topicId: '',
topic: {} as any,
postList: [] as Array<any>,
postPagination: {
page: 1,
pages: 1,
count: 0,
},
sortOrder: 'latest',
}, },
onLoad() { onLoad(options: any) {
app.waitLogin({ type: 1 }).then(() => {}) const topicId = options.id || ''
this.setData({ topicId })
app.waitLogin({ type: 1 }).then(() => {
this.getTopicDetail()
this.getPostList()
})
},
getTopicDetail() {
const { topicId } = this.data
wx.ajax({
method: 'GET',
url: '?r=psvt/topic/detail',
data: { topicId },
}).then((res: any) => {
this.setData({ topic: res.topic || {} })
})
},
getPostList(page = 1, append = false) {
const { topicId, sortOrder } = this.data
wx.ajax({
method: 'GET',
url: '?r=psvt/post/list',
data: {
topicId,
page,
count: 10,
sortOrder,
},
}).then((res: any) => {
const list = res?.list || []
this.setData({
postList: append ? this.data.postList.concat(list) : list,
postPagination: {
page: res?.page || 1,
pages: res?.pages || 1,
count: Number(res?.count) || 0,
},
})
})
},
onReachBottom() {
const { postPagination } = this.data
if (postPagination.page < postPagination.pages) {
this.getPostList(postPagination.page + 1, true)
}
},
onChangeSort(e: any) {
const { sort } = e.currentTarget.dataset
if (sort === this.data.sortOrder) return
this.setData({
sortOrder: sort,
postList: [],
postPagination: {
page: 1,
pages: 1,
count: 0,
},
})
this.getPostList()
},
handleDetail(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/wallDetail/index?id=${id}`,
})
},
handleAdd() {
wx.navigateTo({
url: '/pages/wallForm/index',
})
},
handlePreviewMedia(e: any) {
const { index, attachments } = e.currentTarget.dataset
const sources = (attachments || []).map((att: any) => {
if (att.attachmentType == 2) {
return { url: att.attachmentUrl, type: 'video', poster: att.videoCoverUrl }
}
return { url: att.attachmentUrl, type: 'image' }
})
wx.previewMedia({
current: Number(index) || 0,
sources,
})
}, },
handlePopupOk() { handlePopupOk() {
const { popupType } = this.data const { popupType } = this.data
@ -74,6 +165,9 @@ Page({
url: '/pages/wechatWork/index', url: '/pages/wechatWork/index',
}) })
}, },
handleBack() {
wx.navigateBack()
},
}) })
export {} export {}

75
src/pages/hotTopicDetail/index.wxml

@ -1,5 +1,5 @@
<navbar fixed custom-style="background:{{background}}" back> <navbar fixed custom-style="background:{{background}}" back>
<view class="page-title" slot="left"> <view class="page-title" slot="left" bind:tap="handleBack">
<van-icon name="arrow-left" /> <van-icon name="arrow-left" />
</view> </view>
</navbar> </navbar>
@ -11,64 +11,83 @@
<view class="info"> <view class="info">
<view class="i-header"> <view class="i-header">
<image class="icon" src="{{imageUrl}}icon23.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon23.png?t={{Timestamp}}"></image>
<view class="name">我的心悸经历</view> <view class="name">{{topic.topicName}}</view>
</view> </view>
<view class="total">1200人浏览 | 300篇帖子</view> <view class="total">{{topic.postCount}}篇帖子</view>
</view> </view>
<view class="container"> <view class="container">
<view class="navbar"> <view class="navbar">
<view class="nav-item active">最新</view> <view class="nav-item {{sortOrder==='latest' && 'active'}}" bind:tap="onChangeSort" data-sort="latest">最新</view>
<view class="nav-item">最热</view> <view class="nav-item {{sortOrder==='hot' && 'active'}}" bind:tap="onChangeSort" data-sort="hot">最热</view>
</view> </view>
<view class="list"> <view class="list">
<view class="list-card" bind:tap="handleDetail"> <view class="list-card" wx:for="{{postList}}" wx:key="id" bind:tap="handleDetail" data-id="{{item.id}}">
<view class="user"> <view class="user">
<image class="avatar" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image class="avatar" mode="aspectFill" src="{{item.avatar}}"></image>
<view class="wrap"> <view class="wrap">
<view class="name">@林间心跳</view> <view class="name">@{{item.nickName}}</view>
<view class="date">2026年7月1日 13:09</view> <view class="date">{{item.publishTime}}</view>
</view> </view>
</view> </view>
<view class="hot" style="background: url('{{imageUrl}}bg11.png?t={{Timestamp}}') no-repeat top center/100%">热门推荐</view> <view class="hot" wx:if="{{item.isRecommend===1}}" style="background: url('{{imageUrl}}bg11.png?t={{Timestamp}}') no-repeat top center/100%">热门推荐</view>
<view class="content"> <view class="content">
<view class="c-topic">#我的心悸经历</view> <view class="c-topic" wx:for="{{item.topics}}" wx:key="topicId" wx:for-item="topic">#{{topic.topicName}}</view>
<view class="c-topic">#PSVT急救常识</view> {{item.content}}
今天去复诊,把最近的发作记录都给医生看了,沟通效率高了很多,也更安心。原来平时记下时间和感受真的很有用。
</view> </view>
<view class="tags"> <view class="tags" wx:if="{{item.categories && item.categories.length}}">
<view class="tag" wx:for="{{4}}" wx:key="index">患者故事</view> <view class="tag" wx:for="{{item.categories}}" wx:key="categoryId" wx:for-item="cate">{{cate.categoryName}}</view>
</view> </view>
<!-- 单图 --> <!-- 单图 -->
<image class="photo1" mode="heightFix" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image
<!-- 多图 --> wx:if="{{item.attachments.length===1 && item.attachments[0].attachmentType==1}}"
<view class="photo9"> class="photo1"
<view class="p-item" wx:for="{{3}}" wx:key="index"> mode="heightFix"
<image class="p-img" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> src="{{item.attachments[0].attachmentUrl}}"
</view> catch:tap="handlePreviewMedia"
data-index="0"
data-attachments="{{item.attachments}}"
></image>
<!-- 单视频 -->
<view
wx:if="{{item.attachments.length===1 && item.attachments[0].attachmentType==2}}"
class="photo1 video-cover"
catch:tap="handlePreviewMedia"
data-index="0"
data-attachments="{{item.attachments}}"
>
<image class="p-img" mode="aspectFill" src="{{item.attachments[0].videoCoverUrl}}"></image>
<image class="play-icon" src="{{imageUrl}}icon17.png?t={{Timestamp}}"></image>
</view> </view>
<view class="comment"> <!-- 多图/视频混合 -->
<view class="name">最新评论:</view> <view
我也是,复诊前先整理记录,医生会更容易判断~ wx:if="{{item.attachments.length>1}}"
class="photo9"
>
<view class="p-item" wx:for="{{item.attachments}}" wx:for-item="att" wx:key="id" catch:tap="handlePreviewMedia" data-index="{{index}}" data-attachments="{{item.attachments}}">
<image class="p-img" mode="aspectFill" src="{{att.attachmentType==2 ? att.videoCoverUrl : att.attachmentUrl}}"></image>
<image class="play-icon" wx:if="{{att.attachmentType==2}}" src="{{imageUrl}}icon17.png?t={{Timestamp}}"></image>
</view>
</view> </view>
<view class="footer"> <view class="footer">
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon9.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon9.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{item.likeCount}}</view>
</view> </view>
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{item.viewCount}}</view>
</view> </view>
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{item.commentCount}}</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<pagination pagination="{{postPagination}}" />
</view> </view>
</view> </view>
<view class="fixed-add"> <view class="fixed-add" bind:tap="handleAdd">
<image class="icon" src="{{imageUrl}}icon24.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon24.png?t={{Timestamp}}"></image>
</view> </view>

6
src/pages/index/index.ts

@ -3,10 +3,12 @@ const app = getApp<IAppOption>()
Page({ Page({
data: { data: {
popupShow: false, popupShow: false,
popupType: '', popupType: 'popup5',
popupParams: { popupParams: {
position: 'bottom', position: 'center',
close: true,
} as any, } as any,
userInfo: {} as any, userInfo: {} as any,
days: 0, days: 0,
cateList: [] as Array<{ CateId: string; CateName: string }>, cateList: [] as Array<{ CateId: string; CateName: string }>,

8
src/pages/index/index.wxml

@ -175,3 +175,11 @@
<image class="icon" src="{{imageUrl}}icon12.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon12.png?t={{Timestamp}}"></image>
<view class="name">健康助理</view> <view class="name">健康助理</view>
</view> </view>
<popup
show="{{popupShow}}"
type="{{popupType}}"
params="{{popupParams}}"
bind:ok="handlePopupOk"
bind:cancel="handlePopupCancel"
></popup>

1
src/pages/login/index.scss

@ -110,6 +110,7 @@
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%); background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
border-color: transparent; border-color: transparent;
&::before { &::before {
font-size: 28rpx;
color: #fff; color: #fff;
} }
} }

2
src/pages/my/index.wxml

@ -18,7 +18,7 @@
</view> </view>
</view> </view>
<view class="kkd"> <view class="kkd">
<view class="k-item" bind:tap="handlePDev"> <view class="k-item" bind:tap="routerTo" data-url="/pages/myWall/index">
我的帖子 我的帖子
<image class="icon" src="{{imageUrl}}icon30.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon30.png?t={{Timestamp}}"></image>
</view> </view>

2
src/pages/myComment/index.json

@ -4,7 +4,7 @@
"usingComponents": { "usingComponents": {
"popup": "/components/popup/index", "popup": "/components/popup/index",
"navbar": "/components/navbar/index", "navbar": "/components/navbar/index",
"uploadFile": "/components/uploadFile/index", "pagination": "/components/pagination/index",
"van-popup": "@vant/weapp/popup/index" "van-popup": "@vant/weapp/popup/index"
} }
} }

1
src/pages/myComment/index.scss

@ -88,6 +88,7 @@ page {
height: 96rpx; height: 96rpx;
background: #e2e8f0; background: #e2e8f0;
border-radius: 16rpx; border-radius: 16rpx;
flex-shrink: 0;
} }
.post-desc { .post-desc {

102
src/pages/myComment/index.ts

@ -1,5 +1,13 @@
const app = getApp<IAppOption>() const app = getApp<IAppOption>()
const PAGE_SIZE = 10
const statusClassMap: Record<number, string> = {
1: 'status1',
2: 'status2',
3: 'status3',
}
Page({ Page({
data: { data: {
popupShow: false, popupShow: false,
@ -8,13 +16,72 @@ Page({
position: 'bottom', position: 'bottom',
} as any, } as any,
topicShow: false, commentList: [] as any[],
replayShow: true, commentPagination: {
page: 1,
fileList: [] as any[], pages: 1,
count: 0,
},
},
onShow() {
app.waitLogin({ type: 1 }).then(() => {
this.getMyCommentList()
})
},
getMyCommentList(page = 1, append = false) {
wx.ajax({
method: 'GET',
url: '?r=psvt/my-comment/list',
data: {
page,
count: PAGE_SIZE,
},
}).then((res: any) => {
const list = (res.list || []).map((item: any) => ({
...item,
statusClass: statusClassMap[item.commentStatus] || 'status1',
statusText: item.commentStatusDesc || '',
dateText: item.commentTime || item.submitTime || item.createTime || '',
}))
this.setData({
commentList: append ? this.data.commentList.concat(list) : list,
commentPagination: {
page: res.page || 1,
pages: res.pages || 1,
count: Number(res.count) || 0,
},
})
})
},
onReachBottom() {
const { commentPagination } = this.data
if (commentPagination.page < commentPagination.pages) {
this.getMyCommentList(commentPagination.page + 1, true)
}
},
handleDetail(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/wallDetail/index?id=${id}`,
})
}, },
onLoad() { handleDelete(e: any) {
app.waitLogin({ type: 1 }).then(() => {}) const { id } = e.currentTarget.dataset
wx.showModal({
title: '提示',
content: '确定删除该评论吗?',
success: (res) => {
if (!res.confirm) return
wx.ajax({
method: 'POST',
url: '?r=psvt/my-comment/delete',
data: { commentId: Number(id) },
}).then(() => {
wx.showToast({ title: '删除成功', icon: 'success' })
this.getMyCommentList()
})
},
})
}, },
handlePopupOk() { handlePopupOk() {
const { popupType } = this.data const { popupType } = this.data
@ -74,29 +141,6 @@ Page({
}) })
} }
}, },
handleWechatWork() {
wx.navigateTo({
url: '/pages/wechatWork/index',
})
},
handleSetData(e) {
const { fileList } = this.data
this.setData({
fileList: [...fileList, ...e.detail],
})
},
handleDeleteFile(e) {
const { index } = e.detail
const { fileList } = this.data
this.setData({
fileList: fileList.filter((_, i) => i !== index),
})
},
topicClose() {
this.setData({
topicShow: false,
})
},
handleBack() { handleBack() {
wx.navigateBack() wx.navigateBack()
}, },

32
src/pages/myComment/index.wxml

@ -9,37 +9,33 @@
class="page" class="page"
style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;" style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;"
> >
<view class="list"> <view class="list" wx:if="{{commentList.length}}">
<view class="list-card" bind:tap="handleDetail"> <view class="list-card" wx:for="{{commentList}}" wx:key="id">
<view class="card-header"> <view class="card-header">
<!-- status1:待审核,status2:已通过,status3:已驳回 --> <view class="status-tag {{item.statusClass}}">{{item.statusText}}</view>
<view class="status-tag status1">待审核</view> <view class="time">{{item.dateText}}</view>
<view class="time">2026-01-01 13:00:09</view>
</view> </view>
<view class="card-content">记录发作时间真的很重要,尤其是频率和持续时长</view> <view class="card-content">{{item.content}}</view>
<view class="card-reference"> <view class="card-reference" bind:tap="handleDetail" data-id="{{item.postId}}">
<view class="reference-tags">
<view class="tag"># 我的心悸经历</view>
<view class="tag"># PSVT急救常识</view>
</view>
<view class="reference-post"> <view class="reference-post">
<view class="post-thumbnail"></view> <image class="post-thumbnail" mode="aspectFill" src="{{item.postCoverImage}}" wx:if="{{item.postCoverImage}}"></image>
<view class="post-desc">今天把近期的状态、复诊资料和生活节奏整理了一下,也想记录一下最</view> <view class="post-thumbnail" wx:else></view>
<view class="post-desc">{{item.postTitle}}</view>
</view> </view>
</view> </view>
<view class="card-overlay"> <view class="card-overlay" wx:if="{{item.commentStatus==1}}">
<image class="icon" src="{{imageUrl}}icon40.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon40.png?t={{Timestamp}}"></image>
<view class="overlay-text">评论正在审核中,预计1-2个工作日内完成审核。</view> <view class="overlay-text">评论正在审核中,预计1-2个工作日内完成审核。</view>
</view> </view>
<view class="rejected"> <view class="rejected" wx:if="{{item.commentStatus==3 && item.rejectReason}}">
<image class="icon" src="{{imageUrl}}icon39.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon39.png?t={{Timestamp}}"></image>
<view class="blod">驳回原因:</view> <view class="blod">驳回原因:</view>
语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 {{item.rejectReason}}
语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当
</view> </view>
<view class="card-footer"> <view class="card-footer">
<view class="delete-btn">删除</view> <view class="delete-btn" catch:tap="handleDelete" data-id="{{item.id}}">删除</view>
</view> </view>
</view> </view>
</view> </view>
<pagination pagination="{{commentPagination}}" />
</view> </view>

3
src/pages/myWall/index.json

@ -4,6 +4,7 @@
"popup": "/components/popup/index", "popup": "/components/popup/index",
"navbar": "/components/navbar/index", "navbar": "/components/navbar/index",
"uploadFile": "/components/uploadFile/index", "uploadFile": "/components/uploadFile/index",
"van-popup": "@vant/weapp/popup/index" "van-popup": "@vant/weapp/popup/index",
"pagination": "/components/pagination/index"
} }
} }

37
src/pages/myWall/index.scss

@ -5,10 +5,40 @@ page {
.page-title { .page-title {
font-size: 36rpx; font-size: 36rpx;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
padding-right: 100rpx;
} }
.page { .page {
padding-bottom: 260rpx; padding-bottom: 260rpx;
.type-navbar {
position: sticky;
top: 0;
left: 0;
padding: 48rpx 0 24rpx 30rpx;
display: flex;
flex-wrap: nowrap;
gap: 12rpx;
overflow-x: auto;
overflow-y: hidden;
background-color: #f7fafc;
&::-webkit-scrollbar {
display: none;
}
.type {
flex-shrink: 0;
padding: 12rpx 32rpx;
font-size: 28rpx;
color: rgba(71, 85, 105, 1);
background-color: #fff;
border: 1px solid rgba(241, 245, 249, 1);
border-radius: 16rpx;
line-height: 40rpx;
&.active {
color: #fff;
background: linear-gradient(270deg, #439fc7 0%, #2f82a9 100%);
}
}
}
.list { .list {
padding: 0 30rpx; padding: 0 30rpx;
.list-card { .list-card {
@ -44,6 +74,13 @@ page {
background-color: rgba(45, 190, 141, 0.08); background-color: rgba(45, 190, 141, 0.08);
} }
} }
.c-title {
margin-top: 32rpx;
font-size: 32rpx;
font-weight: bold;
color: rgba(24, 28, 30, 1);
line-height: 52rpx;
}
.content { .content {
margin-top: 32rpx; margin-top: 32rpx;
font-size: 32rpx; font-size: 32rpx;

109
src/pages/myWall/index.ts

@ -1,5 +1,19 @@
const app = getApp<IAppOption>() const app = getApp<IAppOption>()
const statusClassMap: Record<number, string> = {
0: 'status1',
1: 'status2',
2: 'status4',
3: 'status3',
}
const statusTextMap: Record<number, string> = {
0: '草稿',
1: '待审核',
2: '已通过',
3: '已驳回',
}
Page({ Page({
data: { data: {
popupShow: false, popupShow: false,
@ -12,9 +26,97 @@ Page({
replayShow: true, replayShow: true,
fileList: [] as any[], fileList: [] as any[],
postList: [] as any[],
postPagination: {
page: 1,
pages: 1,
count: 0,
},
filterStatus: '' as string | number,
filters: [
{ label: '全部', value: '' },
{ label: '草稿', value: 0 },
{ label: '待审核', value: 1 },
{ label: '已通过', value: 2 },
{ label: '已驳回', value: 3 },
],
},
onShow() {
app.waitLogin({ type: 1 }).then(() => {
this.getMyPostList()
})
},
getMyPostList(page = 1, append = false) {
const { filterStatus } = this.data
wx.ajax({
method: 'GET',
url: '?r=psvt/my-post/list',
data: {
page,
count: 10,
...(filterStatus !== '' ? { postStatus: filterStatus } : {}),
},
}).then((res: any) => {
const list = (res.list || []).map((item: any) => ({
...item,
statusClass: statusClassMap[item.postStatus] || 'status1',
statusText: item.postStatusDesc || statusTextMap[item.postStatus] || '',
dateText: item.publishTime || item.submitTime || item.createTime || '',
imageList: (item.attachments || []).filter((att: any) => att.attachmentType == 1),
}))
this.setData({
postList: append ? this.data.postList.concat(list) : list,
postPagination: {
page: res.page || 1,
pages: res.pages || 1,
count: Number(res.count) || 0,
},
})
})
}, },
onLoad() { onReachBottom() {
app.waitLogin({ type: 1 }).then(() => {}) const { postPagination } = this.data
if (postPagination.page < postPagination.pages) {
this.getMyPostList(postPagination.page + 1, true)
}
},
handleFilterTap(e: any) {
const { value } = e.currentTarget.dataset
this.setData({ filterStatus: value }, () => {
this.getMyPostList()
})
},
handleDetail(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/wallDetail/index?id=${id}`,
})
},
handleDelete(e: any) {
const { id } = e.currentTarget.dataset
wx.showModal({
title: '提示',
content: '确定删除该帖子吗?',
success: (res) => {
if (res.confirm) {
wx.ajax({
method: 'POST',
url: '?r=psvt/my-post/delete',
data: { postId: Number(id) },
}).then(() => {
wx.showToast({ title: '删除成功', icon: 'success' })
this.getMyPostList()
})
}
},
})
},
handleEdit(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/wallForm/index?id=${id}`,
})
}, },
handlePopupOk() { handlePopupOk() {
const { popupType } = this.data const { popupType } = this.data
@ -97,6 +199,9 @@ Page({
topicShow: false, topicShow: false,
}) })
}, },
handleBack() {
wx.navigateBack()
},
}) })
export {} export {}

55
src/pages/myWall/index.wxml

@ -1,5 +1,5 @@
<navbar fixed custom-style="background:{{background}}" back> <navbar fixed custom-style="background:{{background}}" back>
<view class="page-title" slot="left"> <view class="page-title" slot="left" bind:tap="handleBack">
<van-icon name="arrow-left" /> <van-icon name="arrow-left" />
我的帖子 我的帖子
</view> </view>
@ -9,43 +9,58 @@
class="page" class="page"
style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;" style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;"
> >
<!-- <view class="type-navbar"> -->
<!-- <view -->
<!-- class="type {{filterStatus === item.value ? 'active' : ''}}" -->
<!-- wx:for="{{filters}}" -->
<!-- wx:key="label" -->
<!-- data-value="{{item.value}}" -->
<!-- bind:tap="handleFilterTap" -->
<!-- >{{item.label}}</view> -->
<!-- </view> -->
<view class="list"> <view class="list">
<view class="list-card" bind:tap="handleDetail"> <view
class="list-card"
wx:for="{{postList}}"
wx:key="id"
bind:tap="handleDetail"
data-id="{{item.id}}"
>
<!-- 草稿:status1; 待审核 status2; 已驳回 status3; 已通过 status4 --> <!-- 草稿:status1; 待审核 status2; 已驳回 status3; 已通过 status4 -->
<view class="status status1">草稿</view> <view class="status {{item.statusClass}}">{{item.statusText}}</view>
<view class="c-title" wx:if="{{item.title}}">{{item.title}}</view>
<view class="content"> <view class="content">
<view class="c-topic">#我的心悸经历</view> <view class="c-topic" wx:for="{{item.topics}}" wx:key="topicId" wx:for-item="topic">#{{topic.topicName}}</view>
<view class="c-topic">#PSVT急救常识</view> {{item.content}}
今天去复诊,把最近的发作记录都给医生看了,沟通效率高了很多,也更安心。原来平时记下时间和感受真的很有用。
</view> </view>
<view class="tags"> <view class="tags" wx:if="{{item.categories && item.categories.length > 0}}">
<view class="tag" wx:for="{{4}}" wx:key="index">患者故事</view> <view class="tag" wx:for="{{item.categories}}" wx:key="categoryId" wx:for-item="cat">{{cat.categoryName}}</view>
</view> </view>
<!-- 单图 --> <!-- 单图 -->
<image class="photo1" mode="heightFix" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image wx:if="{{item.imageList.length === 1}}" class="photo1" mode="heightFix" src="{{item.imageList[0].attachmentUrl}}"></image>
<!-- 多图 --> <!-- 多图 -->
<view class="photo9"> <view wx:if="{{item.imageList.length > 1}}" class="photo9">
<view class="p-item" wx:for="{{3}}" wx:key="index"> <view class="p-item" wx:for="{{item.imageList}}" wx:key="id" wx:for-item="att">
<image class="p-img" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image class="p-img" mode="aspectFill" src="{{att.attachmentUrl}}"></image>
</view> </view>
</view> </view>
<view class="date">2026年7月1日 13:09</view> <view class="date">{{item.dateText}}</view>
<view class="rejected"> <view class="rejected" wx:if="{{item.postStatus === 3 && item.rejectReason}}">
<image class="icon" src="{{imageUrl}}icon39.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon39.png?t={{Timestamp}}"></image>
<view class="blod">驳回原因:</view> <view class="blod">驳回原因:</view>
语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 {{item.rejectReason}}
语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当 语言不当
</view> </view>
<view class="footer"> <view class="footer">
<view class="public"> <view class="public">
<image class="icon" src="{{imageUrl}}icon37.png?t={{Timestamp}}"></image> <image class="icon" mode="widthFix" src="{{imageUrl}}{{item.visibility == 1 ? 'icon21' : 'icon22'}}.png?t={{Timestamp}}"></image>
公开可见 {{item.visibility == 1 ? '公开可见' : '仅自己可见'}}
</view> </view>
<view class="btn-wrap"> <view class="btn-wrap">
<view class="btn1">删除</view> <view class="btn1" catch:tap="handleDelete" data-id="{{item.id}}" data-index="{{index}}">删除</view>
<view class="btn2">编辑</view> <view class="btn2" catch:tap="handleEdit" data-id="{{item.id}}">编辑</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<pagination pagination="{{postPagination}}" />
</view> </view>

2
src/pages/start/index.ts

@ -8,7 +8,7 @@ Page({
const { doctorId } = parseScene(options.scene) as { doctorId: string } const { doctorId } = parseScene(options.scene) as { doctorId: string }
app.globalData.waitBindDoctorId = doctorId app.globalData.waitBindDoctorId = doctorId
} }
app.waitLogin().then(() => { app.waitLogin({ type: 1 }).then(() => {
wx.reLaunch({ wx.reLaunch({
url: '/pages/index/index', url: '/pages/index/index',
}) })

1
src/pages/userInfo/index.scss

@ -5,6 +5,7 @@ page {
.page-title { .page-title {
font-size: 36rpx; font-size: 36rpx;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
padding-right: 100rpx;
} }
.page { .page {

28
src/pages/userInfo/index.ts

@ -153,19 +153,20 @@ Page({
handleAreaChange(e: any) { handleAreaChange(e: any) {
const [province, city] = e.detail const [province, city] = e.detail
const provinceId = province.value ? Number(province.value) : '' const provinceId = province.value ? Number(province.value) : ''
const cityId = city.value ? Number(city.value) : '' const cityId = city?.value ? Number(city.value) : ''
const cityName = city?.label || ''
this.setData({ this.setData({
provinceId, provinceId,
provinceName: province.label, provinceName: province.label,
cityId, cityId,
cityName: city.label, cityName,
regionText: [province.label, city.label].filter(Boolean).join(' '), regionText: [province.label, cityName].filter(Boolean).join(' '),
}) })
this.submitUpdate({ this.submitUpdate({
provinceId, provinceId,
provinceName: province.label, provinceName: province.label,
cityId, cityId,
cityName: city.label, cityName,
}) })
}, },
submitUpdate(data: Record<string, any>) { submitUpdate(data: Record<string, any>) {
@ -206,6 +207,25 @@ Page({
}, },
}) })
}, },
handleCancellation() {
wx.showModal({
title: '注销确认',
content: '注销后账号将被永久删除,且不可恢复,确定要注销吗?',
confirmColor: '#ba1a1a',
success: (res) => {
if (res.confirm) {
wx.ajax({
method: 'POST',
url: '?r=psvt/user/cancellation',
data: {},
}).then(() => {
wx.showToast({ title: '注销成功', icon: 'success' })
wx.restartMiniProgram({ path: '/pages/start/index' })
})
}
},
})
},
}) })
export {} export {}

4
src/pages/userInfo/index.wxml

@ -55,7 +55,7 @@
> >
<view class="info-row"> <view class="info-row">
<text class="info-label">所在地区</text> <text class="info-label">所在地区</text>
<text class="info-value">{{regionText || ''}}</text> <text class="info-value">{{regionText || '请选择所在地区'}}</text>
<van-icon name="arrow" class="info-arrow" /> <van-icon name="arrow" class="info-arrow" />
</view> </view>
</picker-area> </picker-area>
@ -73,6 +73,6 @@
</view> </view>
</view> </view>
<view class="btn-logout" bind:tap="handleLogout">退出登录</view> <view class="btn-logout" bind:tap="handleLogout">退出登录</view>
<view class="btn-cancel">注销</view> <view class="btn-cancel" bind:tap="handleCancellation">注销</view>
</view> </view>
</view> </view>

3
src/pages/wall/index.json

@ -2,6 +2,7 @@
"navigationStyle": "custom", "navigationStyle": "custom",
"usingComponents": { "usingComponents": {
"popup": "/components/popup/index", "popup": "/components/popup/index",
"navbar": "/components/navbar/index" "navbar": "/components/navbar/index",
"pagination": "/components/pagination/index"
} }
} }

24
src/pages/wall/index.scss

@ -178,6 +178,24 @@ page {
height: 352rpx; height: 352rpx;
border-radius: 24rpx; border-radius: 24rpx;
} }
.video-cover {
position: relative;
overflow: hidden;
.p-img {
width: 100%;
height: 100%;
border-radius: 24rpx;
}
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80rpx;
height: 80rpx;
z-index: 1;
}
.photo9 { .photo9 {
margin-top: 24rpx; margin-top: 24rpx;
display: grid; display: grid;
@ -186,11 +204,17 @@ page {
.p-item { .p-item {
width: 100%; width: 100%;
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
position: relative;
overflow: hidden;
.p-img { .p-img {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 24rpx; border-radius: 24rpx;
} }
.play-icon {
width: 56rpx;
height: 56rpx;
}
} }
} }
.comment { .comment {

150
src/pages/wall/index.ts

@ -7,9 +7,149 @@ Page({
popupParams: { popupParams: {
position: 'bottom', position: 'bottom',
} as any, } as any,
wallConfig: {} as any,
hotTopics: [] as Array<any>,
categoryList: [] as Array<any>,
activeCategoryId: '',
sortOrder: 'latest',
publishButtonEnabled: true,
postList: [] as Array<any>,
postPagination: {
page: 1,
pages: 1,
count: 0,
},
},
onShow() {
app.waitLogin({ type: 1 }).then(() => {
this.getWallConfig()
this.getHotTopics()
this.getCategoryList()
this.getPostList()
})
},
getWallConfig() {
wx.ajax({
method: 'GET',
url: '?r=psvt/wall/get-config',
}).then((res: any) => {
const config = res || {}
config.inputBoxEnabled = config.inputBoxEnabled == 1
config.publishButtonEnabled = config.publishButtonEnabled == 1
this.setData({
wallConfig: config,
publishButtonEnabled: config.publishButtonEnabled,
})
app.globalData.wallConfig = config
const tabBar = this.getTabBar?.() as any
if (tabBar && tabBar.updateWallName) {
tabBar.updateWallName(config.navName || '交流墙')
}
})
},
getHotTopics() {
wx.ajax({
method: 'GET',
url: '?r=psvt/wall/index-topic',
}).then((res: any) => {
this.setData({
hotTopics: res?.list || [],
})
})
}, },
onLoad() { getCategoryList() {
app.waitLogin({ type: 1 }).then(() => {}) wx.ajax({
method: 'GET',
url: '?r=psvt/wall/index-category',
}).then((res: any) => {
this.setData({
categoryList: res?.list || [],
})
})
},
getPostList(page = 1, append = false) {
const { sortOrder, activeCategoryId } = this.data
const params: any = {
page,
count: 10,
sortOrder,
}
if (activeCategoryId) {
params.categoryId = activeCategoryId
}
wx.ajax({
method: 'GET',
url: '?r=psvt/post/list',
data: params,
}).then((res: any) => {
const list = res?.list || []
this.setData({
postList: append ? this.data.postList.concat(list) : list,
postPagination: {
page: res?.page || 1,
pages: res?.pages || 1,
count: Number(res?.count) || 0,
},
})
})
},
onChangeCategory(e: any) {
const { id } = e.currentTarget.dataset
this.setData({
activeCategoryId: id || '',
})
this.getPostList()
},
onChangeSort(e: any) {
const { sort } = e.currentTarget.dataset
if (sort === this.data.sortOrder) return
this.setData({
sortOrder: sort,
})
this.getPostList()
},
onReachBottom() {
const { postPagination } = this.data
if (postPagination.page < postPagination.pages) {
this.getPostList(postPagination.page + 1, true)
}
},
handleDetail(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/wallDetail/index?id=${id}`,
})
},
handleHotTopicMore() {
wx.navigateTo({
url: '/pages/hotTopic/index',
})
},
handleTopicDetail(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/hotTopicDetail/index?id=${id}`,
})
},
handleAdd() {
wx.navigateTo({
url: '/pages/wallForm/index',
})
},
handlePreviewMedia(e: any) {
const { index, attachments } = e.currentTarget.dataset
const sources = (attachments || []).map((att: any) => {
if (att.attachmentType == 2) {
return { url: att.attachmentUrl, type: 'video', poster: att.videoCoverUrl }
}
return { url: att.attachmentUrl, type: 'image' }
})
wx.previewMedia({
current: Number(index) || 0,
sources,
})
}, },
handlePopupOk() { handlePopupOk() {
const { popupType } = this.data const { popupType } = this.data
@ -69,11 +209,7 @@ Page({
}) })
} }
}, },
handleDetail() {
wx.navigateTo({
url: '/pages/wallDetail/index',
})
},
}) })
export {} export {}

84
src/pages/wall/index.wxml

@ -12,72 +12,100 @@
<input class="input" placeholder-class="place-input" type="text" placeholder="搜索帖子 / 话题 / 患者故事" /> <input class="input" placeholder-class="place-input" type="text" placeholder="搜索帖子 / 话题 / 患者故事" />
</view> </view>
</view> </view>
<view class="topic"> <view class="topic" wx:if="{{hotTopics.length}}">
<view class="t-header"> <view class="t-header">
<view class="title">热门话题</view> <view class="title">热门话题</view>
<view class="more"> <view class="more" bind:tap="handleHotTopicMore">
更多 更多
<van-icon name="arrow" /> <van-icon name="arrow" />
</view> </view>
</view> </view>
<view class="t-container"> <view class="t-container">
<view class="item" wx:for="{{4}}" wx:key="index"> <view class="item" wx:for="{{hotTopics}}" wx:key="id" bind:tap="handleTopicDetail" data-id="{{item.id}}">
<view class="shap">#</view> <view class="shap">#</view>
发作时我这样做 {{item.topicName}}
</view> </view>
</view> </view>
</view> </view>
<view class="type-navbar"> <view class="type-navbar">
<view class="type {{index==0 && 'active'}}" wx:for="{{9}}" wx:key="index">最新发布</view> <view class="type {{activeCategoryId==='' && 'active'}}" bind:tap="onChangeCategory" data-id="">全部</view>
<view
class="type {{activeCategoryId===item.id && 'active'}}"
wx:for="{{categoryList}}"
wx:key="id"
bind:tap="onChangeCategory"
data-id="{{item.id}}"
>
{{item.categoryName}}
</view>
</view> </view>
<view class="list"> <view class="list">
<view class="list-card" bind:tap="handleDetail"> <view class="list-card" wx:for="{{postList}}" wx:key="id" bind:tap="handleDetail" data-id="{{item.id}}">
<view class="user"> <view class="user">
<image class="avatar" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image class="avatar" mode="aspectFill" src="{{item.avatar || imageUrl + 'bg1.png?t=' + Timestamp}}"></image>
<view class="wrap"> <view class="wrap">
<view class="name">@林间心跳</view> <view class="name">{{item.nickName}}</view>
<view class="date">2026年7月1日 13:09</view> <view class="date">{{item.publishTime}}</view>
</view> </view>
</view> </view>
<view class="hot" style="background: url('{{imageUrl}}bg11.png?t={{Timestamp}}') no-repeat top center/100%">热门推荐</view> <view class="hot" wx:if="{{item.isRecommend===1}}" style="background: url('{{imageUrl}}bg11.png?t={{Timestamp}}') no-repeat top center/100%">热门推荐</view>
<view class="content"> <view class="content">
<view class="c-topic">#我的心悸经历</view> <view class="c-topic" wx:for="{{item.topics}}" wx:for-item="topic" wx:key="topicId" catch:tap="handleTopicDetail" data-id="{{topic.topicId}}">#{{topic.topicName}}</view>
<view class="c-topic">#PSVT急救常识</view> {{item.content}}
今天去复诊,把最近的发作记录都给医生看了,沟通效率高了很多,也更安心。原来平时记下时间和感受真的很有用。
</view> </view>
<view class="tags"> <view class="tags" wx:if="{{item.categories.length}}">
<view class="tag" wx:for="{{4}}" wx:key="index">患者故事</view> <view class="tag" wx:for="{{item.categories}}" wx:for-item="cat" wx:key="categoryId">{{cat.categoryName}}</view>
</view> </view>
<!-- 单图 --> <!-- 单图 -->
<image class="photo1" mode="heightFix" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image
<!-- 多图 --> wx:if="{{item.attachments.length===1 && item.attachments[0].attachmentType==1}}"
<view class="photo9"> class="photo1"
<view class="p-item" wx:for="{{3}}" wx:key="index"> mode="heightFix"
<image class="p-img" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> src="{{item.attachments[0].attachmentUrl}}"
</view> catch:tap="handlePreviewMedia"
data-index="0"
data-attachments="{{item.attachments}}"
></image>
<!-- 单视频 -->
<view
wx:if="{{item.attachments.length===1 && item.attachments[0].attachmentType==2}}"
class="photo1 video-cover"
catch:tap="handlePreviewMedia"
data-index="0"
data-attachments="{{item.attachments}}"
>
<image class="p-img" mode="aspectFill" src="{{item.attachments[0].videoCoverUrl}}"></image>
<image class="play-icon" src="{{imageUrl}}icon17.png?t={{Timestamp}}"></image>
</view> </view>
<view class="comment"> <!-- 多图/视频混合 -->
<view class="name">最新评论:</view> <view
我也是,复诊前先整理记录,医生会更容易判断~ wx:if="{{item.attachments.length>1}}"
class="photo9"
>
<view class="p-item" wx:for="{{item.attachments}}" wx:for-item="att" wx:key="id" catch:tap="handlePreviewMedia" data-index="{{index}}" data-attachments="{{item.attachments}}">
<image class="p-img" mode="aspectFill" src="{{att.attachmentType==2 ? att.videoCoverUrl : att.attachmentUrl}}"></image>
<image class="play-icon" wx:if="{{att.attachmentType==2}}" src="{{imageUrl}}icon17.png?t={{Timestamp}}"></image>
</view>
</view> </view>
<view class="footer"> <view class="footer">
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon9.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon9.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{item.likeCount || 0}}</view>
</view> </view>
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{item.viewCount || 0}}</view>
</view> </view>
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{item.commentCount || 0}}</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<pagination pagination="{{postPagination}}" />
</view> </view>
<view class="fixed-add"> <view class="fixed-add" wx:if="{{publishButtonEnabled}}" bind:tap="handleAdd">
<image class="icon" src="{{imageUrl}}icon24.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon24.png?t={{Timestamp}}"></image>
</view> </view>

1
src/pages/wallDetail/index.json

@ -3,6 +3,7 @@
"usingComponents": { "usingComponents": {
"popup": "/components/popup/index", "popup": "/components/popup/index",
"navbar": "/components/navbar/index", "navbar": "/components/navbar/index",
"pagination": "/components/pagination/index",
"van-action-sheet": "@vant/weapp/action-sheet/index", "van-action-sheet": "@vant/weapp/action-sheet/index",
"van-popup": "@vant/weapp/popup/index" "van-popup": "@vant/weapp/popup/index"
} }

51
src/pages/wallDetail/index.scss

@ -5,19 +5,41 @@ page {
.page-title { .page-title {
font-size: 36rpx; font-size: 36rpx;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
padding-right: 100rpx;
} }
.page { .page {
padding-bottom: 230rpx; padding-bottom: 230rpx;
.info-card { .info-card {
margin: 30rpx; margin: 30rpx;
padding: 36rpx 32rpx 36rpx; padding: 0 32rpx 36rpx;
border-radius: 32rpx; border-radius: 32rpx;
border: 2rpx solid #f1f5f9; border: 2rpx solid #f1f5f9;
background: #fff; background: #fff;
box-shadow: 0 16rpx 48rpx -8rpx rgba(0, 99, 132, 0.06); box-shadow: 0 16rpx 48rpx -8rpx rgba(0, 99, 132, 0.06);
box-sizing: border-box; box-sizing: border-box;
.auth {
padding: 34rpx 0;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid rgba(241, 245, 249, 1);
.a-status {
display: flex;
align-items: center;
gap: 16rpx;
.icon {
width: 36rpx;
height: 36rpx;
}
}
.a-content {
font-size: 28rpx;
color: rgba(47, 130, 169, 1);
}
}
.user { .user {
padding-top: 36rpx;
display: flex; display: flex;
gap: 24rpx; gap: 24rpx;
.avatar { .avatar {
@ -71,6 +93,24 @@ page {
height: 352rpx; height: 352rpx;
border-radius: 24rpx; border-radius: 24rpx;
} }
.video-cover {
position: relative;
overflow: hidden;
.p-img {
width: 100%;
height: 100%;
border-radius: 24rpx;
}
}
.play-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80rpx;
height: 80rpx;
z-index: 1;
}
.photo9 { .photo9 {
margin-top: 24rpx; margin-top: 24rpx;
display: grid; display: grid;
@ -79,11 +119,17 @@ page {
.p-item { .p-item {
width: 100%; width: 100%;
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
position: relative;
overflow: hidden;
.p-img { .p-img {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 24rpx; border-radius: 24rpx;
} }
.play-icon {
width: 56rpx;
height: 56rpx;
}
} }
} }
.comment { .comment {
@ -152,7 +198,6 @@ page {
.w-header { .w-header {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
gap: 30rpx; gap: 30rpx;
flex-wrap: wrap; flex-wrap: wrap;
.name { .name {
@ -456,7 +501,7 @@ page {
} }
} }
} }
.a-close{ .a-close {
padding: 32rpx; padding: 32rpx;
font-size: 32rpx; font-size: 32rpx;
line-height: 48rpx; line-height: 48rpx;

274
src/pages/wallDetail/index.ts

@ -1,5 +1,7 @@
const app = getApp<IAppOption>() const app = getApp<IAppOption>()
const PAGE_SIZE = 10
Page({ Page({
data: { data: {
popupShow: false, popupShow: false,
@ -11,9 +13,253 @@ Page({
topicShow: false, topicShow: false,
replayShow: false, replayShow: false,
actionShow: false, actionShow: false,
postId: '',
post: {} as any,
isMyPost: false,
commentList: [] as Array<any>,
commentPagination: {
page: 1,
pages: 1,
count: 0,
},
predefinedComments: [] as Array<any>,
replyContent: '',
replyToCommentId: 0,
replyToUserName: '',
inputBoxEnabled: true,
}, },
onLoad() { onLoad(options: any) {
app.waitLogin({ type: 1 }).then(() => {}) if (options.id) {
this.setData({ postId: options.id })
}
const wallConfig = app.globalData.wallConfig || {}
this.setData({ inputBoxEnabled: wallConfig.inputBoxEnabled === true || wallConfig.inputBoxEnabled == 1 })
app.waitLogin({ type: 1 }).then(() => {
this.getPostDetail()
this.getCommentList()
this.viewPost()
})
},
getPostDetail() {
const { postId } = this.data
wx.ajax({
method: 'GET',
url: '?r=psvt/post/detail',
data: { id: Number(postId) },
}).then((res: any) => {
const post = res || {}
app.getUserInfo().then((userInfo: any) => {
const isMyPost = userInfo?.userId && post.userId && userInfo.userId === post.userId
this.setData({ post, isMyPost })
})
})
},
getCommentList(page = 1, append = false) {
const { postId } = this.data
wx.ajax({
method: 'GET',
url: '?r=psvt/comment/list',
data: { postId: Number(postId), page, count: PAGE_SIZE },
}).then((res: any) => {
const list = res?.list || []
this.setData({
commentList: append ? this.data.commentList.concat(list) : list,
commentPagination: {
page: res?.page || 1,
pages: res?.pages || 1,
count: Number(res?.count) || 0,
},
})
})
},
viewPost() {
const { postId } = this.data
wx.ajax({
method: 'POST',
url: '?r=psvt/post/view',
data: { postId: Number(postId) },
loading: false,
showMsg: false,
})
},
handleLike() {
if (!app.checkLoginType(1, true)) return
const { post } = this.data
if (post.hasLiked) return
wx.ajax({
method: 'POST',
url: '?r=psvt/post/like',
data: { targetId: Number(post.id), targetType: 1 },
}).then(() => {
this.setData({
'post.hasLiked': true,
'post.likeCount': (post.likeCount || 0) + 1,
})
})
},
handleCommentLike(e: any) {
if (!app.checkLoginType(1, true)) return
const { id, index, childIndex } = e.currentTarget.dataset
const { commentList } = this.data
if (childIndex !== undefined) {
const child = commentList[index].childComments[childIndex]
if (child.hasLiked) return
wx.ajax({
method: 'POST',
url: '?r=psvt/comment/like',
data: { targetId: Number(id) },
}).then(() => {
this.setData({
[`commentList[${index}].childComments[${childIndex}].hasLiked`]: true,
[`commentList[${index}].childComments[${childIndex}].likeCount`]: (child.likeCount || 0) + 1,
})
})
} else {
const comment = commentList[index]
if (comment.hasLiked) return
wx.ajax({
method: 'POST',
url: '?r=psvt/comment/like',
data: { targetId: Number(id) },
}).then(() => {
this.setData({
[`commentList[${index}].hasLiked`]: true,
[`commentList[${index}].likeCount`]: (comment.likeCount || 0) + 1,
})
})
}
},
showReplay(e: any) {
const { id, nickName } = e.currentTarget.dataset
const data: any = {
replayShow: true,
replyToCommentId: id || 0,
replyToUserName: nickName || '',
replyContent: '',
}
this.setData(data)
this.getPredefinedComments()
},
getPredefinedComments() {
wx.ajax({
method: 'GET',
url: '?r=psvt/comment/predefined-list',
loading: false,
showMsg: false,
}).then((res: any) => {
this.setData({
predefinedComments: res?.list || [],
})
})
},
replayClose() {
this.setData({
replayShow: false,
replyContent: '',
replyToCommentId: 0,
replyToUserName: '',
})
},
onReplyInput(e: any) {
this.setData({
replyContent: e.detail.value,
})
},
handleSendComment() {
const { postId, replyContent, replyToCommentId } = this.data
if (!replyContent.trim()) {
wx.showToast({ title: '请输入评论内容', icon: 'none' })
return
}
const data: any = {
postId: Number(postId),
content: replyContent,
}
if (replyToCommentId > 0) {
data.replyToCommentId = Number(replyToCommentId)
}
wx.ajax({
method: 'POST',
url: '?r=psvt/comment/create',
data,
}).then(() => {
wx.showToast({ title: '评论成功', icon: 'success' })
this.replayClose()
this.getCommentList()
})
},
handlePredefinedTap(e: any) {
const { content } = e.currentTarget.dataset
if (!content) return
if (this.data.inputBoxEnabled) {
this.setData({ replyContent: content })
} else {
const { postId, replyToCommentId } = this.data
const data: any = {
postId: Number(postId),
content,
}
if (replyToCommentId > 0) {
data.replyToCommentId = Number(replyToCommentId)
}
wx.ajax({
method: 'POST',
url: '?r=psvt/comment/create',
data,
}).then(() => {
wx.showToast({ title: '评论成功', icon: 'success' })
this.replayClose()
this.getCommentList()
})
}
},
showAction() {
this.setData({ actionShow: true })
},
actionClose() {
this.setData({ actionShow: false })
},
handleEdit() {
const { postId } = this.data
wx.navigateTo({
url: `/pages/wallForm/index?id=${postId}`,
})
this.actionClose()
},
handleDelete() {
const { postId } = this.data
wx.showModal({
title: '提示',
content: '确定删除该帖子吗?',
success: (res) => {
if (!res.confirm) return
wx.ajax({
method: 'POST',
url: '?r=psvt/my-post/delete',
data: { postId: Number(postId) },
}).then(() => {
wx.showToast({ title: '删除成功', icon: 'success' })
setTimeout(() => {
wx.navigateBack()
}, 1000)
})
},
})
this.actionClose()
},
onReachBottom() {
const { commentPagination } = this.data
if (commentPagination.page < commentPagination.pages) {
this.getCommentList(commentPagination.page + 1, true)
}
},
topicClose() {
this.setData({
topicShow: false,
})
}, },
handlePopupOk() { handlePopupOk() {
const { popupType } = this.data const { popupType } = this.data
@ -78,10 +324,26 @@ Page({
url: '/pages/wechatWork/index', url: '/pages/wechatWork/index',
}) })
}, },
handlePreviewMedia(e: any) {
topicClose() { const { index, attachments } = e.currentTarget.dataset
this.setData({ const sources = (attachments || []).map((att: any) => {
topicShow: false, if (att.attachmentType == 2) {
return { url: att.attachmentUrl, type: 'video', poster: att.videoCoverUrl }
}
return { url: att.attachmentUrl, type: 'image' }
})
wx.previewMedia({
current: Number(index) || 0,
sources,
})
},
handleBack() {
wx.navigateBack()
},
handleTopicDetail(e: any) {
const { id } = e.currentTarget.dataset
wx.navigateTo({
url: `/pages/hotTopicDetail/index?id=${id}`,
}) })
}, },
}) })

242
src/pages/wallDetail/index.wxml

@ -1,5 +1,5 @@
<navbar fixed custom-style="background:{{background}}" back> <navbar fixed custom-style="background:{{background}}" back>
<view class="page-title" slot="left"> <view class="page-title" slot="left" bind:tap="handleBack">
<van-icon name="arrow-left" /> <van-icon name="arrow-left" />
帖子详情 帖子详情
</view> </view>
@ -10,184 +10,228 @@
style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;" style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100% 478rpx;padding-top: {{pageTop}}px;"
> >
<view class="info-card"> <view class="info-card">
<view class="auth" wx:if="{{isMyPost}}" bind:tap="showAction">
<view class="a-status">
<image
class="icon"
mode="widthFix"
src="{{imageUrl}}{{post.visibility===2 ? 'icon22' : 'icon37'}}.png?t={{Timestamp}}"
></image>
<view class="status">{{post.visibility===2 ? '仅自己可见' : '公开可见'}}</view>
</view>
<view class="a-content">
编辑和设置权限
<van-icon name="arrow" />
</view>
</view>
<view class="user"> <view class="user">
<image class="avatar" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image class="avatar" mode="aspectFill" src="{{post.avatar || imageUrl + 'bg1.png?t=' + Timestamp}}"></image>
<view class="wrap"> <view class="wrap">
<view class="name">@林间心跳</view> <view class="name">{{post.nickName}}</view>
<view class="date">2026年7月1日 13:09</view> <view class="date">{{post.publishTime}}</view>
</view> </view>
</view> </view>
<view class="content"> <view class="content">
<view class="c-topic">#我的心悸经历</view> <view
<view class="c-topic">#PSVT急救常识</view> class="c-topic"
今天去复诊,把最近的发作记录都给医生看了,沟通效率高了很多,也更安心。原来平时记下时间和感受真的很有用。 wx:for="{{post.topics}}"
wx:for-item="topic"
wx:key="topicId"
bind:tap="handleTopicDetail"
data-id="{{topic.topicId}}"
>
#{{topic.topicName}}
</view>
{{post.content}}
</view> </view>
<view class="tags"> <view class="tags" wx:if="{{post.categories.length}}">
<view class="tag" wx:for="{{4}}" wx:key="index">患者故事</view> <view class="tag" wx:for="{{post.categories}}" wx:for-item="cat" wx:key="categoryId">{{cat.categoryName}}</view>
</view> </view>
<!-- 单图 --> <!-- 单图 -->
<image class="photo1" mode="heightFix" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image
<!-- 多图 --> wx:if="{{post.attachments.length===1 && post.attachments[0].attachmentType==1}}"
<view class="photo9"> class="photo1"
<view class="p-item" wx:for="{{3}}" wx:key="index"> mode="heightFix"
<image class="p-img" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> src="{{post.attachments[0].attachmentUrl}}"
</view> bind:tap="handlePreviewMedia"
data-index="0"
data-attachments="{{post.attachments}}"
></image>
<!-- 单视频 -->
<view
wx:if="{{post.attachments.length===1 && post.attachments[0].attachmentType==2}}"
class="photo1 video-cover"
bind:tap="handlePreviewMedia"
data-index="0"
data-attachments="{{post.attachments}}"
>
<image class="p-img" mode="aspectFill" src="{{post.attachments[0].videoCoverUrl}}"></image>
<image class="play-icon" src="{{imageUrl}}icon17.png?t={{Timestamp}}"></image>
</view> </view>
<view class="comment"> <!-- 多图/视频混合 -->
<view class="name">最新评论:</view> <view wx:if="{{post.attachments.length>1}}" class="photo9">
我也是,复诊前先整理记录,医生会更容易判断~ <view
class="p-item"
wx:for="{{post.attachments}}"
wx:for-item="att"
wx:key="id"
bind:tap="handlePreviewMedia"
data-index="{{index}}"
data-attachments="{{post.attachments}}"
>
<image
class="p-img"
mode="aspectFill"
src="{{att.attachmentType==2 ? att.videoCoverUrl : att.attachmentUrl}}"
></image>
<image class="play-icon" wx:if="{{att.attachmentType==2}}" src="{{imageUrl}}icon17.png?t={{Timestamp}}"></image>
</view>
</view> </view>
</view> </view>
<view class="comment-list"> <view class="comment-list">
<view class="c-header"> <view class="c-header">
精彩评论 精彩评论
<view class="total">20</view> <view class="total">{{commentPagination.count}}</view>
</view> </view>
<view class="list"> <view class="list">
<view class="list-card"> <view class="list-card" wx:for="{{commentList}}" wx:for-index="pIndex" wx:key="id">
<view class="comment-item"> <view class="comment-item">
<image class="avatar" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> <image class="avatar" mode="aspectFill" src="{{item.avatar || imageUrl + 'bg1.png?t=' + Timestamp}}"></image>
<view class="wrap"> <view class="wrap">
<view class="w-header"> <view class="w-header">
<view class="name">小鹿</view> <view class="name">{{item.nickName}}</view>
<view class="date">2026-01-01 12:00:00</view> <view class="date">{{item.commentTime}}</view>
</view> </view>
<view class="content">我也是,复诊前先整理记录,医生会更容易</view> <view class="content">{{item.content}}</view>
<view class="op-wrap"> <view class="op-wrap">
<view class="op"> <view class="op" bind:tap="handleCommentLike" data-id="{{item.id}}" data-index="{{pIndex}}">
<image class="icon" src="{{imageUrl}}icon9.png?t={{Timestamp}}"></image> <image
<view class="num">126</view> class="icon"
src="{{imageUrl}}{{item.hasLiked ? 'icon9-1' : 'icon9'}}.png?t={{Timestamp}}"
></image>
<view class="num">{{item.likeCount || 0}}</view>
</view> </view>
<view class="op"> <view class="op" bind:tap="showReplay" data-id="{{item.id}}" data-nick-name="{{item.nickName}}">
<image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">回复</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<view class="replay-list"> <view class="replay-list" wx:if="{{item.childComments.length}}">
<view class="comment-item"> <view
<image class="avatar" mode="aspectFill" src="{{imageUrl}}bg1.png?t={{Timestamp}}"></image> class="comment-item"
wx:for="{{item.childComments}}"
wx:for-item="childItem"
wx:for-index="cIndex"
wx:key="id"
>
<image
class="avatar"
mode="aspectFill"
src="{{childItem.avatar || imageUrl + 'bg1.png?t=' + Timestamp}}"
></image>
<view class="wrap"> <view class="wrap">
<view class="w-header"> <view class="w-header">
<view class="name">小鹿</view> <view class="name">{{childItem.nickName}}</view>
<view class="date">2026-01-01 12:00:00</view> <view class="date">{{childItem.commentTime}}</view>
</view> </view>
<view class="content"> <view class="content">
<view class="replay-name">回复小红:</view> <view class="replay-name" wx:if="{{childItem.replyToUserName}}">
我也是,复诊前先整理记录,医生会更容易 回复{{childItem.replyToUserName}}:
</view>
{{childItem.content}}
</view> </view>
<view class="op-wrap"> <view class="op-wrap">
<view class="op"> <view
<image class="icon" src="{{imageUrl}}icon9.png?t={{Timestamp}}"></image> class="op"
<view class="num">126</view> bind:tap="handleCommentLike"
data-id="{{childItem.id}}"
data-index="{{pIndex}}"
data-child-index="{{cIndex}}"
>
<image
class="icon"
src="{{imageUrl}}{{childItem.hasLiked ? 'icon9-1' : 'icon9'}}.png?t={{Timestamp}}"
></image>
<view class="num">{{childItem.likeCount || 0}}</view>
</view> </view>
<view class="op"> <view class="op" bind:tap="showReplay" data-id="{{item.id}}" data-nick-name="{{childItem.nickName}}">
<image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">回复</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<view class="replay-flod-btn">
<view class="line"></view>
收起
<van-icon name="arrow-up" />
</view>
</view> </view>
</view> </view>
</view> </view>
<pagination pagination="{{commentPagination}}" />
</view> </view>
<view class="page-footer"> <view class="page-footer">
<view class="auth" wx:if="{{false}}"> <view class="comment" bind:tap="showReplay">我要评论</view>
<image class="icon" mode="widthFix" src="{{imageUrl}}icon37.png?t={{Timestamp}}"></image>
<view class="wrap">
<view class="status">公开可见</view>
<view class="content">
编辑和设置权限
<van-icon name="arrow" />
</view>
</view>
</view>
<view class="auth" wx:elif="{{false}}">
<image class="icon" mode="widthFix" src="{{imageUrl}}icon22.png?t={{Timestamp}}"></image>
<view class="wrap">
<view class="status">仅自己可见</view>
<view class="content">
编辑和设置权限
<van-icon name="arrow" />
</view>
</view>
</view>
<view class="comment" wx:else>我要评论</view>
<view class="op-wrap"> <view class="op-wrap">
<view class="op"> <view class="op" bind:tap="handleLike">
<image class="icon" src="{{imageUrl}}icon9.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}{{post.hasLiked ? 'icon9-active' : 'icon9'}}.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{post.likeCount || 0}}</view>
</view> </view>
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon8.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{post.viewCount || 0}}</view>
</view> </view>
<view class="op"> <view class="op">
<image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon15.png?t={{Timestamp}}"></image>
<view class="num">126</view> <view class="num">{{post.commentCount || 0}}</view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
<van-popup show="{{ topicShow }}" position="bottom" round bind:close="topicClose">
<view class="topic-popup">
<view class="tp-header">
<view class="title">热门话题</view>
<van-icon class="cross" name="cross" />
</view>
<view class="tp-list">
<view class="li" wx:for="{{10}}" wx:key="index">
<view class="num">{{index+1}}</view>
<view class="content"># 我的心悸经历</view>
<view class="total">230篇帖子</view>
</view>
</view>
</view>
</van-popup>
<van-popup show="{{ replayShow }}" position="bottom" round bind:close="replayClose"> <van-popup show="{{ replayShow }}" position="bottom" round bind:close="replayClose">
<view class="replay-popup"> <view class="replay-popup">
<view class="title">我要评论</view> <view class="title" wx:if="{{replyToCommentId}}">回复</view>
<view class="title" wx:else>我要评论</view>
<view class="quick"> <view class="quick">
<view class="q-item active">加油,你真棒!</view> <view
<view class="q-item">相同的经历,感同身受</view> class="q-item"
wx:for="{{predefinedComments}}"
wx:key="id"
bind:tap="handlePredefinedTap"
data-content="{{item.content}}"
>
{{item.content}}
</view>
</view> </view>
<textarea <textarea
wx:if="{{inputBoxEnabled}}"
class="txa" class="txa"
placeholder-class="txa-place" placeholder-class="txa-place"
maxlength="{{500}}" maxlength="{{500}}"
placeholder="请输入" placeholder="{{replyToCommentId > 0 ? '回复@' + replyToUserName + ':' : '请输入'}}"
data-place="回复@小红:" value="{{replyContent}}"
bindinput="onReplyInput"
></textarea> ></textarea>
<view class="footer"> <view class="footer">
<view class="cancel">取消</view> <view class="cancel" bind:tap="replayClose">取消</view>
<view class="send">发送</view> <view class="send" wx:if="{{inputBoxEnabled}}" bind:tap="handleSendComment">发送</view>
</view> </view>
</view> </view>
</van-popup> </van-popup>
<van-popup show="{{ actionShow }}" round position="bottom" round bind:close="actionClose"> <van-popup show="{{ actionShow }}" round position="bottom" bind:close="actionClose">
<view class="action-popup"> <view class="action-popup">
<view class="a-list"> <view class="a-list">
<view class="row"> <view class="row" bind:tap="handleEdit">
<image class="icon" src="{{imageUrl}}icon41.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon41.png?t={{Timestamp}}"></image>
编辑 编辑
</view> </view>
<view class="row"> <view class="row" bind:tap="handleDelete">
<image class="icon" src="{{imageUrl}}icon42.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon42.png?t={{Timestamp}}"></image>
删除 删除
</view> </view>
</view> </view>
<view class="a-close">取消</view> <view class="a-close" bind:tap="actionClose">取消</view>
</view> </view>
</van-popup> </van-popup>

98
src/pages/wallForm/index.scss

@ -5,6 +5,7 @@ page {
.page-title { .page-title {
font-size: 36rpx; font-size: 36rpx;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
padding-right: 100rpx;
} }
.page { .page {
@ -42,6 +43,20 @@ page {
.title-place { .title-place {
color: rgba(148, 163, 184, 1); color: rgba(148, 163, 184, 1);
} }
.selected-topics {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
padding-top: 16rpx;
.s-item {
height: 52rpx;
line-height: 52rpx;
border-radius: 14rpx;
font-size: 32rpx;
color: rgba(47, 130, 169, 1);
box-sizing: border-box;
}
}
.txa { .txa {
padding: 32rpx 0; padding: 32rpx 0;
font-size: 32rpx; font-size: 32rpx;
@ -76,6 +91,10 @@ page {
width: 32rpx; width: 32rpx;
height: 32rpx; height: 32rpx;
} }
&.active {
background: rgba(47, 130, 169, 0.08);
border-color: rgba(47, 130, 169, 1);
}
} }
} }
.wrap { .wrap {
@ -238,3 +257,82 @@ page {
} }
} }
} }
.topic-popup {
.tp-header {
margin: 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
.title {
font-size: 44rpx;
color: rgba(30, 41, 59, 1);
font-weight: bold;
}
.cross {
font-size: 44rpx;
color: rgba(148, 163, 184, 1);
}
}
.tp-list {
max-height: 928rpx;
overflow-y: auto;
overflow-x: hidden;
padding: 0 40rpx;
&::-webkit-scrollbar {
display: none;
}
.li {
padding: 32rpx 0;
display: flex;
gap: 40rpx;
border-bottom: 1px solid #f8fafc;
.num {
flex-shrink: 0;
width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #f7fafc;
border-radius: 14rpx;
}
.content {
padding-top: 4rpx;
flex: 1;
font-size: 36rpx;
color: rgba(30, 41, 59, 1);
line-height: 48rpx;
}
.total {
padding-top: 8rpx;
font-size: 32rpx;
color: rgba(148, 163, 184, 1);
line-height: 40rpx;
}
}
.li.active {
.content {
color: rgba(47, 130, 169, 1);
}
}
.li:nth-of-type(1) {
.num {
background-color: rgba(255, 107, 34, 1);
color: #fff;
}
}
.li:nth-of-type(2) {
.num {
background-color: rgba(255, 143, 51, 1);
color: #fff;
}
}
.li:nth-of-type(3) {
.num {
background-color: rgba(47, 130, 169, 1);
color: #fff;
}
}
}
}

187
src/pages/wallForm/index.ts

@ -12,9 +12,189 @@ Page({
replayShow: false, replayShow: false,
fileList: [] as any[], fileList: [] as any[],
topicList: [] as any[],
categoryList: [] as any[],
selectedTopicIds: [] as number[],
selectedTopics: [] as any[],
selectedCategoryIds: [] as number[],
title: '',
content: '',
visibility: 1,
isEdit: false,
editPostId: '',
},
onLoad(options: any) {
if (options && options.id) {
this.setData({ isEdit: true, editPostId: options.id })
}
app.waitLogin({ type: 1 }).then(() => {
this.getTopicList()
this.getCategoryList()
if (this.data.isEdit) {
this.getPostDetail()
}
})
},
getTopicList() {
wx.ajax({
method: 'GET',
url: '?r=psvt/topic/top-list',
}).then((res: any) => {
this.setData({
topicList: res.list || [],
selectedTopics: this.computeSelectedTopics(this.data.selectedTopicIds, res.list || []),
})
})
}, },
onLoad() { getCategoryList() {
app.waitLogin({ type: 1 }).then(() => {}) wx.ajax({
method: 'GET',
url: '?r=psvt/wall/index-category',
}).then((res: any) => {
this.setData({
categoryList: res.list || [],
})
})
},
getPostDetail() {
const { editPostId } = this.data
wx.ajax({
method: 'GET',
url: '?r=psvt/post/detail',
data: { id: editPostId },
}).then((res: any) => {
if (!res) return
const fileList = (res.attachments || []).map((item: any) => {
if (item.attachmentType == 2) {
return {
fileType: 'video',
url: item.videoCoverUrl || '',
videoUrl: item.attachmentUrl || '',
}
}
return {
fileType: 'image',
url: item.attachmentUrl || '',
}
})
this.setData({
title: res.title || '',
content: res.content || '',
visibility: res.visibility || 1,
selectedTopicIds: (res.topics || []).map((t: any) => t.topicId),
selectedTopics: this.computeSelectedTopics(
(res.topics || []).map((t: any) => t.topicId),
this.data.topicList,
),
selectedCategoryIds: (res.categories || []).map((c: any) => c.categoryId),
fileList,
})
})
},
onTitleInput(e: any) {
this.setData({ title: e.detail.value })
},
onContentInput(e: any) {
this.setData({ content: e.detail.value })
},
handleTopicTap(e: any) {
const id = Number(e.currentTarget.dataset.id)
const selectedTopicIds = [...this.data.selectedTopicIds]
const index = selectedTopicIds.indexOf(id)
if (index > -1) {
selectedTopicIds.splice(index, 1)
} else {
selectedTopicIds.push(id)
}
this.setData({
selectedTopicIds,
selectedTopics: this.computeSelectedTopics(selectedTopicIds, this.data.topicList),
})
},
handleRemoveTopic(e: any) {
const id = Number(e.currentTarget.dataset.id)
const selectedTopicIds = this.data.selectedTopicIds.filter((tid) => tid !== id)
this.setData({
selectedTopicIds,
selectedTopics: this.computeSelectedTopics(selectedTopicIds, this.data.topicList),
})
},
computeSelectedTopics(ids: number[], list: any[]) {
return ids.map((id) => list.find((t) => String(t.id) === String(id))).filter((t) => t)
},
showTopicPopup() {
this.setData({ topicShow: true })
},
topicPopupClose() {
this.setData({ topicShow: false })
},
handleCategoryTap(e: any) {
const id = Number(e.currentTarget.dataset.id)
const selectedCategoryIds = [...this.data.selectedCategoryIds]
const index = selectedCategoryIds.indexOf(id)
if (index > -1) {
selectedCategoryIds.splice(index, 1)
} else {
selectedCategoryIds.push(id)
}
this.setData({ selectedCategoryIds })
},
handleVisibilityChange(e: any) {
this.setData({ visibility: Number(e.currentTarget.dataset.value) })
},
buildAttachments() {
const { fileList } = this.data
return fileList.map((item: any, index: number) => {
const isVideo = item.fileType === 'video'
const attachment: any = {
attachmentType: isVideo ? 2 : 1,
attachmentUrl: isVideo ? item.videoUrl : item.url,
sortOrder: index,
}
if (isVideo) {
attachment.videoCoverUrl = item.url
}
return attachment
})
},
handleSaveDraft() {
this.submitPost(0)
},
handleSubmit() {
this.submitPost(1)
},
submitPost(postStatus: number) {
const { title, content, visibility, selectedTopicIds, selectedCategoryIds, isEdit, editPostId } = this.data
if (!title.trim()) {
wx.showToast({ title: '请添加标题', icon: 'none' })
return
}
const data: any = {
title,
content,
postStatus,
visibility,
topicIds: selectedTopicIds,
categoryIds: selectedCategoryIds,
attachments: this.buildAttachments(),
}
if (isEdit) {
data.postId = Number(editPostId)
}
wx.ajax({
method: 'POST',
url: isEdit ? '?r=psvt/my-post/update' : '?r=psvt/post/create',
data,
}).then(() => {
wx.showToast({
title: postStatus === 0 ? '草稿已保存' : '发布成功',
icon: 'success',
})
setTimeout(() => {
wx.navigateBack()
}, 1000)
})
}, },
handlePopupOk() { handlePopupOk() {
const { popupType } = this.data const { popupType } = this.data
@ -92,6 +272,9 @@ Page({
fileList: fileList.filter((_, i) => i !== index), fileList: fileList.filter((_, i) => i !== index),
}) })
}, },
handleBack() {
wx.navigateBack()
},
}) })
export {} export {}

52
src/pages/wallForm/index.wxml

@ -1,5 +1,7 @@
<wxs module="tools" src="/utils/tools.wxs" />
<navbar fixed custom-style="background:{{background}}" back> <navbar fixed custom-style="background:{{background}}" back>
<view class="page-title" slot="left"> <view class="page-title" slot="left" bind:tap="handleBack">
<van-icon name="arrow-left" /> <van-icon name="arrow-left" />
发布帖子 发布帖子
</view> </view>
@ -11,27 +13,34 @@
> >
<view class="form"> <view class="form">
<view class="upload-wrap"> <view class="upload-wrap">
<uploadFile fileList="{{fileList}}" bind:setData="handleSetData" bind:deleteFile="handleDeleteFile"></uploadFile> <uploadFile fileList="{{fileList}}" fileType="mix" maxNum="{{9}}" bind:setData="handleSetData" bind:deleteFile="handleDeleteFile"></uploadFile>
</view>
<input type="text" class="title" placeholder-class="title-place" placeholder="请添加标题" maxlength="{{50}}" value="{{title}}" bindinput="onTitleInput" />
<view class="selected-topics" wx:if="{{selectedTopics.length}}">
<view class="s-item" wx:for="{{selectedTopics}}" wx:key="id" bind:tap="handleRemoveTopic" data-id="{{item.id}}">
#{{item.topicName}}
</view>
</view> </view>
<input type="text" class="title" placeholder-class="title-place" placeholder="请添加标题" maxlength="{{50}}" />
<textarea <textarea
class="txa" class="txa"
auto-height auto-height
placeholder-class="txa-place" placeholder-class="txa-place"
placeholder="分享你的经历、心得或问题,帮助更多病友..." placeholder="分享你的经历、心得或问题,帮助更多病友..."
value="{{content}}"
bindinput="onContentInput"
></textarea> ></textarea>
<view class="topic-list"> <view class="topic-list">
<view class="t-item" wx:for="{{5}}" wx:key="index"> <view class="t-item" wx:for="{{topicList}}" wx:key="id" bind:tap="handleTopicTap" data-id="{{item.id}}">
<image class="icon" src="{{imageUrl}}icon18.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon18.png?t={{Timestamp}}"></image>
我的心悸经历 {{item.topicName}}
</view> </view>
</view> </view>
<view class="wrap"> <view class="wrap">
<view class="topic-btn"> <view class="topic-btn" bind:tap="showTopicPopup">
<image class="icon" src="{{imageUrl}}icon18.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon18.png?t={{Timestamp}}"></image>
话题 话题
</view> </view>
<view class="total">0/2000</view> <view class="total">{{content.length || 0}}/2000</view>
</view> </view>
</view> </view>
<view class="tag-module"> <view class="tag-module">
@ -40,7 +49,7 @@
内容标签 内容标签
</view> </view>
<view class="tag-wrap"> <view class="tag-wrap">
<view class="tag {{index==0 && 'active'}}" wx:for="{{8}}" wx:key="index">疾病知识</view> <view class="tag {{tools.include(item.id, selectedCategoryIds) ? 'active' : ''}}" wx:for="{{categoryList}}" wx:key="id" bind:tap="handleCategoryTap" data-id="{{item.id}}">{{item.categoryName}}</view>
</view> </view>
</view> </view>
<view class="view-module"> <view class="view-module">
@ -49,15 +58,15 @@
谁可以看 谁可以看
</view> </view>
<view class="v-wrap"> <view class="v-wrap">
<view class="item active"> <view class="item {{visibility === 1 ? 'active' : ''}}" bind:tap="handleVisibilityChange" data-value="1">
<image class="icon" src="{{imageUrl}}icon21.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}{{visibility === 1 ? 'icon21-active' : 'icon21'}}.png?t={{Timestamp}}"></image>
<view class="wrap"> <view class="wrap">
<view class="name">公开</view> <view class="name">公开</view>
<view class="content">所有用户可见</view> <view class="content">所有用户可见</view>
</view> </view>
</view> </view>
<view class="item"> <view class="item {{visibility === 2 ? 'active' : ''}}" bind:tap="handleVisibilityChange" data-value="2">
<image class="icon" src="{{imageUrl}}icon22.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}{{visibility === 2 ? 'icon22-active' : 'icon22'}}.png?t={{Timestamp}}"></image>
<view class="wrap"> <view class="wrap">
<view class="name">私密</view> <view class="name">私密</view>
<view class="content">仅自己可见</view> <view class="content">仅自己可见</view>
@ -66,13 +75,28 @@
</view> </view>
</view> </view>
<view class="page-footer"> <view class="page-footer">
<view class="cancel"> <view class="cancel" bind:tap="handleSaveDraft">
<image class="icon" src="{{imageUrl}}icon26.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon26.png?t={{Timestamp}}"></image>
存草稿 存草稿
</view> </view>
<view class="send"> <view class="send" bind:tap="handleSubmit">
<image class="icon" src="{{imageUrl}}icon27.png?t={{Timestamp}}"></image> <image class="icon" src="{{imageUrl}}icon27.png?t={{Timestamp}}"></image>
发布 发布
</view> </view>
</view> </view>
</view> </view>
<van-popup show="{{topicShow}}" position="bottom" round bind:close="topicPopupClose">
<view class="topic-popup">
<view class="tp-header">
<view class="title">热门话题</view>
<van-icon class="cross" name="cross" bind:tap="topicPopupClose" />
</view>
<view class="tp-list">
<view class="li {{tools.include(item.id, selectedTopicIds) ? 'active' : ''}}" wx:for="{{topicList}}" wx:key="id" bind:tap="handleTopicTap" data-id="{{item.id}}">
<view class="num">{{index+1}}</view>
<view class="content"># {{item.topicName}}</view>
</view>
</view>
</view>
</van-popup>

76
src/pages/wechatWork/index.scss

@ -4,56 +4,58 @@ page {
.page-title { .page-title {
font-size: 36rpx; font-size: 36rpx;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
padding-right: 100rpx;
} }
.page { .page {
.container { .container {
margin: 240rpx 30rpx 0; margin: 120rpx 30rpx 0;
height: 1018rpx; height: 1310rpx;
padding-top: 106rpx; padding-top: 300rpx;
box-sizing: border-box; box-sizing: border-box;
.title { .title {
margin-top: 236rpx; margin: 0 32rpx;
font-size: 40rpx;
color: rgba(30, 41, 59, 1);
font-weight: bold;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; gap: 24rpx;
gap: 12rpx; .avatar {
.icon1, width: 100rpx;
.icon2 { height: 100rpx;
flex-shrink: 0; border-radius: 50%;
width: 94rpx;
height: 12rpx;
} }
.icon1 { .name {
transform: rotate(180deg); font-size: 36rpx;
color: rgba(30, 41, 59, 1);
} }
} }
.code-wrap { .code {
margin: 30rpx auto 0; margin: 120rpx auto 0;
display: block; display: block;
width: 376rpx; width: 340rpx;
height: 376rpx; height: 340rpx;
display: flex;
align-items: center;
justify-content: center;
.code {
width: 340rpx;
height: 340rpx;
}
} }
.btn { .guide {
display: block; margin: 188rpx 32rpx 0;
margin: 20rpx auto 0; padding: 32rpx;
padding-top: 26rpx; border-radius: 24rpx;
text-align: center; background: #f7fafc;
font-size: 36rpx; .row {
color: rgba(255, 255, 255, 1); display: flex;
width: 332rpx; gap: 14rpx;
height: 88rpx; .aside {
box-sizing: border-box; margin-top: 20rpx;
flex-shrink: 0;
width: 18rpx;
height: 18rpx;
border-radius: 50%;
background-color: rgba(47, 130, 169, 1);
}
.content {
font-size: 32rpx;
color: rgba(30, 41, 59, 1);
line-height: 56rpx;
}
}
} }
} }
} }

30
src/pages/wechatWork/index.ts

@ -1,8 +1,32 @@
const _app = getApp<IAppOption>() const app = getApp<IAppOption>()
Page({ Page({
data: {}, data: {
onLoad() {}, avatar: '',
wecomName: '',
qrCode: '',
textList: [] as string[],
},
onLoad() {
app.waitLogin({ type: 1 }).then(() => {
this.getWecomConfig()
})
},
getWecomConfig() {
wx.ajax({
method: 'GET',
url: '?r=psvt/wall/get-wecom-config',
}).then((res: any) => {
const config = res || {}
const textList = [config.text1, config.text2, config.text3, config.text4, config.text5].filter(Boolean)
this.setData({
avatar: config.avatar || '',
wecomName: config.wecomName || '',
qrCode: config.qrCode || '',
textList,
})
})
},
handleBack() { handleBack() {
wx.navigateBack() wx.navigateBack()
}, },

19
src/pages/wechatWork/index.wxml

@ -4,16 +4,21 @@
</view> </view>
</navbar> </navbar>
<view class="page" style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100%;padding-top: {{pageTop}}rpx;"> <view
class="page"
style="background: url('{{imageUrl}}bg6.png?t={{Timestamp}}') no-repeat top center/100%;padding-top: {{pageTop}}rpx;"
>
<view class="container" style="background: url('{{imageUrl}}bg7.png?t={{Timestamp}}') no-repeat top center/100%"> <view class="container" style="background: url('{{imageUrl}}bg7.png?t={{Timestamp}}') no-repeat top center/100%">
<view class="title"> <view class="title">
<image class="icon1" src="{{imageUrl}}icon13.png?t={{Timestamp}}"></image> <image class="avatar" src="{{avatar}}" mode="aspectFill"></image>
添加你的专属客服 <view class="name">{{wecomName}}</view>
<image class="icon2" src="{{imageUrl}}icon13.png?t={{Timestamp}}"></image>
</view> </view>
<view class="code-wrap" style="background: url('{{imageUrl}}bg8.png?t={{Timestamp}}') no-repeat top center/100%"> <image class="code" src="{{qrCode}}" mode="aspectFill" show-menu-by-longpress></image>
<image class="code" src="{{imageUrl}}bg1.png?t={{Timestamp}}" show-menu-by-longpress></image> <view class="guide">
<view class="row" wx:for="{{textList}}" wx:key="index">
<view class="aside"></view>
<view class="content">{{item}}</view>
</view>
</view> </view>
<view class="btn" style="background: url('{{imageUrl}}btn1.png?t={{Timestamp}}') no-repeat top center/100%">长按识别二维码</view>
</view> </view>
</view> </view>

3
src/utils/tools.wxs

@ -1,6 +1,6 @@
function include(value, arr) { function include(value, arr) {
for (var i = 0; i < arr.length; i++) { for (var i = 0; i < arr.length; i++) {
if (arr[i] === value) { if (arr[i] == value) {
return true return true
} }
} }
@ -9,4 +9,5 @@ function include(value, arr) {
module.exports = { module.exports = {
include: include, include: include,
inArray: include,
} }

2
typings/index.d.ts vendored

@ -45,7 +45,7 @@ interface IAppOption {
} }
getUserInfo: (type?: 0 | 1 | 2) => Promise<any> getUserInfo: (type?: 0 | 1 | 2) => Promise<any>
startLogin: (callback?: () => void) => void startLogin: (callback?: () => void) => void
waitLogin: (params?: { type?: 1 | 'any' }) => Promise<void> waitLogin: (params?: { type?: 1 | 'any' }) => Promise<boolean>
checkLoginType: (type: 1 | 'any', confirm: boolean) => boolean checkLoginType: (type: 1 | 'any', confirm: boolean) => boolean
mpBehavior: (data: { PageName: string; type?: 0 | 1 | 2 | 3 }) => void mpBehavior: (data: { PageName: string; type?: 0 | 1 | 2 | 3 }) => void
globalSystemInfo?: globalSystemInfo globalSystemInfo?: globalSystemInfo

Loading…
Cancel
Save