From 861ee7cdacd5527b82f06fe9ddb34848df089200 Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期三, 19 八月 2026 16:09:39 +0800
Subject: [PATCH] feat0806: 管理后台设置微赞直播间密码

---
 traditionalApp/pages/home/home.vue                                                                                    |   28 +++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/service/LiveRoomSettingService.java              |   51 +++++++
 tcm-training-admin/config/routes.ts                                                                                   |    5 
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/dto/LiveRoomSettingSaveRequest.java              |   20 ++
 tcm-training-admin/src/pages/live/live-room-setting/index.tsx                                                         |  107 +++++++++++++++
 tcm-training-admin/src/pages/live/live-room-setting/style.less                                                        |    8 +
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/bootstrap/DataInitializer.java                        |    5 
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/vo/LiveRoomSettingVO.java                        |   13 +
 tcm-training-admin-backend/sql/20260806_admin.sql                                                                     |   14 ++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/entity/LiveRoomSetting.java                      |   19 ++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/controller/LiveRoomSettingController.java        |   35 +++++
 traditionalApp/pages/home/service.js                                                                                  |    6 
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java |   23 +++
 tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/mapper/LiveRoomSettingMapper.java                |   19 ++
 tcm-training-admin/src/pages/live/live-room-setting/service.ts                                                        |   15 ++
 15 files changed, 362 insertions(+), 6 deletions(-)

diff --git a/tcm-training-admin-backend/sql/20260806_admin.sql b/tcm-training-admin-backend/sql/20260806_admin.sql
index 9e15f81..44219cd 100644
--- a/tcm-training-admin-backend/sql/20260806_admin.sql
+++ b/tcm-training-admin-backend/sql/20260806_admin.sql
@@ -60,3 +60,17 @@
 INSERT INTO sys_menu (id, parent_id, name, menu_code, type, sort, status, visible, permission_key, remark, deleted)
 VALUES (5045, 2118, '编辑', 'member_clinic_edit', 'B', 1, 1, 1, 'member:clinic:edit', '编辑诊所按钮', 0)
 ON DUPLICATE KEY UPDATE permission_key = VALUES(permission_key), deleted = 0;
+
+-- 微赞直播间单例设置:仅保留 Banner 图片地址和直播间地址两个业务字段
+CREATE TABLE IF NOT EXISTS tcm_live_room_setting (
+    banner_image_url VARCHAR(500) NOT NULL COMMENT 'Banner图片地址',
+    live_room_url VARCHAR(500) NULL COMMENT '直播间地址'
+) COMMENT='微赞直播间设置';
+
+INSERT INTO sys_menu (id, parent_id, name, menu_code, type, path, component, icon, sort, status, visible, permission_key, remark, deleted)
+VALUES (6000, 3050, '微赞直播间设置', 'live_room_setting', 'C', '/live/course/live-room-setting', 'Live/LiveRoomSetting', NULL, 18, 1, 1, 'live:room-setting:view', '微赞直播间设置菜单', 0)
+ON DUPLICATE KEY UPDATE parent_id = VALUES(parent_id), name = VALUES(name), menu_code = VALUES(menu_code), type = VALUES(type), path = VALUES(path), component = VALUES(component), sort = VALUES(sort), status = VALUES(status), visible = VALUES(visible), permission_key = VALUES(permission_key), remark = VALUES(remark), deleted = 0;
+
+INSERT INTO sys_menu (id, parent_id, name, menu_code, type, path, component, icon, sort, status, visible, permission_key, remark, deleted)
+VALUES (6001, 6000, '编辑微赞直播间设置', 'live_room_setting_edit', 'B', NULL, NULL, NULL, 1, 1, 1, 'live:room-setting:edit', '微赞直播间设置编辑权限', 0)
+ON DUPLICATE KEY UPDATE parent_id = VALUES(parent_id), name = VALUES(name), menu_code = VALUES(menu_code), type = VALUES(type), sort = VALUES(sort), status = VALUES(status), visible = VALUES(visible), permission_key = VALUES(permission_key), remark = VALUES(remark), deleted = 0;
\ No newline at end of file
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java
new file mode 100644
index 0000000..e84d27c
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/app/home/controller/AppLiveRoomSettingController.java
@@ -0,0 +1,23 @@
+package com.tcm.training.admin.app.home.controller;
+
+import com.tcm.training.admin.common.ApiResponse;
+import com.tcm.training.admin.live.service.LiveRoomSettingService;
+import com.tcm.training.admin.live.vo.LiveRoomSettingVO;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping({"/api/app/home", "/api/v1/app/home"})
+public class AppLiveRoomSettingController {
+    private final LiveRoomSettingService service;
+
+    public AppLiveRoomSettingController(LiveRoomSettingService service) {
+        this.service = service;
+    }
+
+    @GetMapping("/live-room-setting")
+    public ApiResponse<LiveRoomSettingVO> current() {
+        return ApiResponse.ok(service.current());
+    }
+}
\ No newline at end of file
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/bootstrap/DataInitializer.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/bootstrap/DataInitializer.java
index e978459..54101d5 100644
--- a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/bootstrap/DataInitializer.java
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/bootstrap/DataInitializer.java
@@ -269,7 +269,10 @@
                 null, null, null, 16, "live:session:delete", "直播场次删除按钮"));
         menuIds.add(saveMenu(3077L, 3060L, "推送开播提醒", "live_session_push_notification", "B",
                 null, null, null, 17, "live:session:push-notification", "直播场次推送提醒按钮"));
-        menuIds.add(saveMenu(3080L, 3050L, "直播预约记录", "live_reserve_view", "C",
+        menuIds.add(saveMenu(6000L, 3050L, "微赞直播间设置", "live_room_setting", "C",
+                "/live/course/live-room-setting", "Live/LiveRoomSetting", null, 18, "live:room-setting:view", "微赞直播间设置菜单"));
+        menuIds.add(saveMenu(6001L, 6000L, "编辑微赞直播间设置", "live_room_setting_edit", "B",
+                null, null, null, 1, "live:room-setting:edit", "微赞直播间设置编辑按钮"));        menuIds.add(saveMenu(3080L, 3050L, "直播预约记录", "live_reserve_view", "C",
                 "/live/course/reserve-records", "Live/ReserveRecords", null, 20, "live:reserve:view", "直播预约记录菜单"));
         menuIds.add(saveMenu(3081L, 3050L, "直播观看记录", "live_watch_view", "C",
                 "/live/course/watch-records", "Live/WatchRecords", null, 21, "live:watch:view", "直播观看记录菜单"));
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/controller/LiveRoomSettingController.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/controller/LiveRoomSettingController.java
new file mode 100644
index 0000000..7bda57a
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/controller/LiveRoomSettingController.java
@@ -0,0 +1,35 @@
+package com.tcm.training.admin.live.controller;
+
+import com.tcm.training.admin.common.ApiResponse;
+import com.tcm.training.admin.live.dto.LiveRoomSettingSaveRequest;
+import com.tcm.training.admin.live.service.LiveRoomSettingService;
+import com.tcm.training.admin.live.vo.LiveRoomSettingVO;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+@Validated
+@RestController
+@RequestMapping({"/api/v1", "/api"})
+public class LiveRoomSettingController {
+    private final LiveRoomSettingService service;
+
+    public LiveRoomSettingController(LiveRoomSettingService service) {
+        this.service = service;
+    }
+
+    @GetMapping("/live-room-settings/current")
+    public ApiResponse<LiveRoomSettingVO> current() {
+        return ApiResponse.ok(service.current());
+    }
+
+    @PutMapping("/live-room-settings/current")
+    public ApiResponse<LiveRoomSettingVO> save(@Valid @RequestBody LiveRoomSettingSaveRequest request) {
+        return ApiResponse.ok(service.save(request));
+    }
+}
\ No newline at end of file
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/dto/LiveRoomSettingSaveRequest.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/dto/LiveRoomSettingSaveRequest.java
new file mode 100644
index 0000000..ad8b5d5
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/dto/LiveRoomSettingSaveRequest.java
@@ -0,0 +1,20 @@
+package com.tcm.training.admin.live.dto;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class LiveRoomSettingSaveRequest {
+    @NotBlank(message = "请上传直播间 Banner 图片")
+    @Size(max = 500, message = "Banner 图片地址不能超过500个字符")
+    private String bannerImageUrl;
+
+    @Size(max = 500, message = "直播间地址不能超过500个字符")
+    private String liveRoomUrl;
+}
\ No newline at end of file
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/entity/LiveRoomSetting.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/entity/LiveRoomSetting.java
new file mode 100644
index 0000000..9ec66f4
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/entity/LiveRoomSetting.java
@@ -0,0 +1,19 @@
+package com.tcm.training.admin.live.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@TableName("tcm_live_room_setting")
+@Getter
+@Setter
+@NoArgsConstructor
+public class LiveRoomSetting {
+    @TableField("banner_image_url")
+    private String bannerImageUrl;
+
+    @TableField("live_room_url")
+    private String liveRoomUrl;
+}
\ No newline at end of file
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/mapper/LiveRoomSettingMapper.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/mapper/LiveRoomSettingMapper.java
new file mode 100644
index 0000000..617366d
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/mapper/LiveRoomSettingMapper.java
@@ -0,0 +1,19 @@
+package com.tcm.training.admin.live.mapper;
+
+import com.tcm.training.admin.live.entity.LiveRoomSetting;
+import org.apache.ibatis.annotations.Insert;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+@Mapper
+public interface LiveRoomSettingMapper {
+    @Select("SELECT banner_image_url, live_room_url FROM tcm_live_room_setting LIMIT 1")
+    LiveRoomSetting selectCurrent();
+
+    @Insert("INSERT INTO tcm_live_room_setting (banner_image_url, live_room_url) VALUES (#{bannerImageUrl}, #{liveRoomUrl})")
+    int insertCurrent(LiveRoomSetting setting);
+
+    @Update("UPDATE tcm_live_room_setting SET banner_image_url = #{bannerImageUrl}, live_room_url = #{liveRoomUrl}")
+    int updateCurrent(LiveRoomSetting setting);
+}
\ No newline at end of file
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/service/LiveRoomSettingService.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/service/LiveRoomSettingService.java
new file mode 100644
index 0000000..7626ffc
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/service/LiveRoomSettingService.java
@@ -0,0 +1,51 @@
+package com.tcm.training.admin.live.service;
+
+import com.tcm.training.admin.live.dto.LiveRoomSettingSaveRequest;
+import com.tcm.training.admin.live.entity.LiveRoomSetting;
+import com.tcm.training.admin.live.mapper.LiveRoomSettingMapper;
+import com.tcm.training.admin.live.vo.LiveRoomSettingVO;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class LiveRoomSettingService {
+    private final LiveRoomSettingMapper mapper;
+
+    public LiveRoomSettingService(LiveRoomSettingMapper mapper) {
+        this.mapper = mapper;
+    }
+
+    public LiveRoomSettingVO current() {
+        return toVO(mapper.selectCurrent());
+    }
+
+    @Transactional
+    public LiveRoomSettingVO save(LiveRoomSettingSaveRequest request) {
+        LiveRoomSetting setting = mapper.selectCurrent();
+        if (setting == null) {
+            setting = new LiveRoomSetting();
+            fill(setting, request);
+            mapper.insertCurrent(setting);
+        } else {
+            fill(setting, request);
+            mapper.updateCurrent(setting);
+        }
+        return toVO(setting);
+    }
+
+    private void fill(LiveRoomSetting setting, LiveRoomSettingSaveRequest request) {
+        setting.setBannerImageUrl(request.getBannerImageUrl().trim());
+        String liveRoomUrl = request.getLiveRoomUrl();
+        setting.setLiveRoomUrl(liveRoomUrl == null || liveRoomUrl.trim().isEmpty() ? null : liveRoomUrl.trim());
+    }
+
+    private LiveRoomSettingVO toVO(LiveRoomSetting setting) {
+        if (setting == null) {
+            return null;
+        }
+        LiveRoomSettingVO vo = new LiveRoomSettingVO();
+        vo.setBannerImageUrl(setting.getBannerImageUrl());
+        vo.setLiveRoomUrl(setting.getLiveRoomUrl());
+        return vo;
+    }
+}
\ No newline at end of file
diff --git a/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/vo/LiveRoomSettingVO.java b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/vo/LiveRoomSettingVO.java
new file mode 100644
index 0000000..71730e4
--- /dev/null
+++ b/tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/vo/LiveRoomSettingVO.java
@@ -0,0 +1,13 @@
+package com.tcm.training.admin.live.vo;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+public class LiveRoomSettingVO {
+    private String bannerImageUrl;
+    private String liveRoomUrl;
+}
\ No newline at end of file
diff --git a/tcm-training-admin/config/routes.ts b/tcm-training-admin/config/routes.ts
index e886c59..44bcc0c 100644
--- a/tcm-training-admin/config/routes.ts
+++ b/tcm-training-admin/config/routes.ts
@@ -314,6 +314,11 @@
             component: './live/broadcast/edit',
           },
           {
+            name: '微赞直播间设置',
+            path: '/live/course/live-room-setting',
+            access: 'live:room-setting:view',
+            component: './live/live-room-setting',
+          },          {
             name: '直播预约记录',
             path: '/live/course/reserve-records',
             access: 'live:reserve:view',
diff --git a/tcm-training-admin/src/pages/live/live-room-setting/index.tsx b/tcm-training-admin/src/pages/live/live-room-setting/index.tsx
new file mode 100644
index 0000000..69f46ad
--- /dev/null
+++ b/tcm-training-admin/src/pages/live/live-room-setting/index.tsx
@@ -0,0 +1,107 @@
+import { PlusOutlined } from '@ant-design/icons';
+import { history, useAccess } from '@umijs/max';
+import { Button, Form, Image, Input, Spin, Upload, message } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { getLiveRoomSetting, saveLiveRoomSetting, uploadLiveRoomBanner } from './service';
+import './style.less';
+
+type FormValues = { bannerImageUrl?: string; liveRoomUrl?: string };
+
+const LiveRoomSettingPage: React.FC = () => {
+  const access = useAccess();
+  const [form] = Form.useForm<FormValues>();
+  const [loading, setLoading] = useState(false);
+  const [submitting, setSubmitting] = useState(false);
+  const [uploading, setUploading] = useState(false);
+  const canView = access['live:room-setting:view'];
+  const canEdit = access['live:room-setting:edit'];
+  const bannerImageUrl = Form.useWatch('bannerImageUrl', form);
+
+  const loadCurrent = async () => {
+    setLoading(true);
+    try {
+      const res = await getLiveRoomSetting();
+      form.setFieldsValue({
+        bannerImageUrl: res.data?.bannerImageUrl || '',
+        liveRoomUrl: res.data?.liveRoomUrl || '',
+      });
+    } catch (error) {
+      message.error((error as Error)?.message || '直播间设置加载失败');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    if (!canView) {
+      history.replace('/Welcome');
+      return;
+    }
+    loadCurrent();
+  }, [canView]);
+
+  const handleSubmit = async () => {
+    if (!canEdit || submitting) return;
+    const values = await form.validateFields();
+    setSubmitting(true);
+    try {
+      await saveLiveRoomSetting({
+        bannerImageUrl: values.bannerImageUrl!,
+        liveRoomUrl: values.liveRoomUrl?.trim() || undefined,
+      });
+      message.success('保存成功');
+      await loadCurrent();
+    } catch (error) {
+      message.error((error as Error)?.message || '保存失败,请稍后重试');
+    } finally {
+      setSubmitting(false);
+    }
+  };
+
+  return (
+    <div className="live-room-setting-page">
+      <div className="live-room-setting-card">
+        <div className="live-room-setting-title">微赞直播间设置</div>
+        <Spin spinning={loading}>
+          <Form form={form} layout="vertical" disabled={loading || !canEdit} className="live-room-setting-form">
+            <Form.Item label="Banner图片" name="bannerImageUrl" rules={[{ required: true, message: '请上传Banner图片' }]}>
+              <Input type="hidden" />
+            </Form.Item>
+            <div className="live-room-setting-upload-row">
+              {bannerImageUrl ? <Image src={bannerImageUrl} className="live-room-setting-preview" /> : null}
+              <Upload
+                accept="image/*"
+                showUploadList={false}
+                customRequest={async (options) => {
+                  setUploading(true);
+                  try {
+                    const res = await uploadLiveRoomBanner(options.file as File);
+                    const url = res.data?.url;
+                    if (!url) throw new Error('上传失败');
+                    form.setFieldValue('bannerImageUrl', url);
+                    await form.validateFields(['bannerImageUrl']);
+                    options.onSuccess?.(res);
+                  } catch (error) {
+                    message.error((error as Error)?.message || '图片上传失败');
+                    options.onError?.(error as Error);
+                  } finally {
+                    setUploading(false);
+                  }
+                }}
+              >
+                <Button icon={<PlusOutlined />} loading={uploading}>上传图片</Button>
+              </Upload>
+              <span className="live-room-setting-tip">建议尺寸 750 × 320px,支持 JPG、PNG</span>
+            </div>
+            <Form.Item label="直播间地址" name="liveRoomUrl" rules={[{ type: 'url', message: '请输入正确的直播间地址' }, { max: 500 }]}>
+              <Input placeholder="请输入微赞直播间地址(可不填)" allowClear />
+            </Form.Item>
+            <Button type="primary" loading={submitting} disabled={!canEdit} onClick={handleSubmit}>保存</Button>
+          </Form>
+        </Spin>
+      </div>
+    </div>
+  );
+};
+
+export default LiveRoomSettingPage;
\ No newline at end of file
diff --git a/tcm-training-admin/src/pages/live/live-room-setting/service.ts b/tcm-training-admin/src/pages/live/live-room-setting/service.ts
new file mode 100644
index 0000000..47c96e6
--- /dev/null
+++ b/tcm-training-admin/src/pages/live/live-room-setting/service.ts
@@ -0,0 +1,15 @@
+import { request } from '@umijs/max';
+
+export async function getLiveRoomSetting() {
+  return request('/live-room-settings/current', { method: 'GET' });
+}
+
+export async function saveLiveRoomSetting(data: { bannerImageUrl: string; liveRoomUrl?: string }) {
+  return request('/live-room-settings/current', { method: 'PUT', data });
+}
+
+export async function uploadLiveRoomBanner(file: File) {
+  const formData = new FormData();
+  formData.append('file', file);
+  return request('/upload/image', { method: 'POST', data: formData });
+}
\ No newline at end of file
diff --git a/tcm-training-admin/src/pages/live/live-room-setting/style.less b/tcm-training-admin/src/pages/live/live-room-setting/style.less
new file mode 100644
index 0000000..0283bb0
--- /dev/null
+++ b/tcm-training-admin/src/pages/live/live-room-setting/style.less
@@ -0,0 +1,8 @@
+.live-room-setting-page { min-height: calc(100vh - 48px); padding: 20px; background: #f2f5f9; }
+.live-room-setting-card { min-height: 620px; padding: 24px; border-radius: 10px; background: #fff; box-shadow: 0 2px 10px rgba(15, 23, 42, .04); }
+.live-room-setting-title { margin-bottom: 28px; color: #303133; font-size: 18px; font-weight: 500; }
+.live-room-setting-form { width: 620px; }
+.live-room-setting-upload-row { display: flex; align-items: center; gap: 16px; margin: -14px 0 28px; }
+.live-room-setting-preview { width: 300px; height: 128px; border-radius: 8px; object-fit: cover; border: 1px solid #ebeef5; }
+.live-room-setting-tip { color: #909399; font-size: 13px; }
+@media (max-width: 800px) { .live-room-setting-form { width: 100%; } .live-room-setting-upload-row { align-items: flex-start; flex-direction: column; } }
\ No newline at end of file
diff --git a/traditionalApp/pages/home/home.vue b/traditionalApp/pages/home/home.vue
index 294a93d..81f37c4 100644
--- a/traditionalApp/pages/home/home.vue
+++ b/traditionalApp/pages/home/home.vue
@@ -43,7 +43,7 @@
 			<view class="quick-entry-row">
 				<view class="quick-entry-card" hover-class="quick-entry-card--pressed" hover-stay-time="80"
 					@tap="goWeiZanLive">
-					<image src="/static/image/home/weizan-live.png" mode="aspectFill" class="quick-entry-card__img"></image>
+					<image :src="weiZanLiveBanner || '/static/image/home/weizan-live.png'" mode="aspectFill" class="quick-entry-card__img"></image>
 				</view>
 				<view class="quick-entry-card" hover-class="quick-entry-card--pressed" hover-stay-time="80"
 					@tap="goOperatorTraining">
@@ -141,7 +141,7 @@
 <script setup lang="ts">
 	import { computed, ref, onMounted } from "vue";
 	import { onPullDownRefresh, onShow } from "@dcloudio/uni-app";
-	import { getBannerList, getHomeFeaturedContent, getHomeLiveCourseList } from "./service";
+	import { getBannerList, getHomeFeaturedContent, getHomeLiveCourseList, getLiveRoomSetting } from "./service";
 
 	interface HomeBannerItem {
 		id : number | string;
@@ -201,7 +201,8 @@
 		const systemInfo = uni.getSystemInfoSync();
 		return String(systemInfo.platform || '').toLowerCase() === 'ios';
 	});
-	const weiZanLiveH5Url = 'https://www.vzan.com';
+	const weiZanLiveH5Url = ref('');
+	const weiZanLiveBanner = ref('');
 
 	const operatorTrainingLiveCourse = computed(() => {
 		const list = liveCourseList.value;
@@ -254,6 +255,18 @@
 	/**
 	 * 请求首页顶部 banner 列表。
 	 */
+	const loadLiveRoomSetting = async () => {
+		try {
+			const res = await getLiveRoomSetting();
+			if (res?.code === 0 && res.data) {
+				weiZanLiveBanner.value = res.data.bannerImageUrl || '';
+				weiZanLiveH5Url.value = res.data.liveRoomUrl || '';
+			}
+		} catch {
+			weiZanLiveBanner.value = '';
+			weiZanLiveH5Url.value = '';
+		}
+	};
 	const loadBannerList = async () => {
 		try {
 			const res = await getBannerList();
@@ -315,11 +328,15 @@
 	}
 
 	/**
-	 * 跳转微赞直播 H5 页面(后端接口暂未提供,先使用静态链接)。
+	 * 跳转后台配置的微赞直播 H5 页面。
 	 */
 	const goWeiZanLive = () => {
+		if (!weiZanLiveH5Url.value) {
+			uni.showToast({ title: '直播间地址暂未配置', icon: 'none' });
+			return;
+		}
 		uni.navigateTo({
-			url: `/pages/webview/webview?url=${encodeURIComponent(weiZanLiveH5Url)}&title=${encodeURIComponent('微赞直播')}`
+			url: `/pages/webview/webview?url=${encodeURIComponent(weiZanLiveH5Url.value)}&title=${encodeURIComponent('微赞直播')}`
 		});
 	};
 
@@ -399,6 +416,7 @@
 		try {
 			await Promise.allSettled([
 				loadBannerList(),
+				loadLiveRoomSetting(),
 				loadHomeLiveCourseList(),
 				loadFeaturedContent()
 			]);
diff --git a/traditionalApp/pages/home/service.js b/traditionalApp/pages/home/service.js
index bd4d223..1a3851e 100644
--- a/traditionalApp/pages/home/service.js
+++ b/traditionalApp/pages/home/service.js
@@ -42,3 +42,9 @@
 export const getNoticeList = (params) => {
 	return request.get(`/index/getNoticeList`, params, 1)
 }
+/**
+ * 获取 APP 首页微赞直播间配置。
+ */
+export const getLiveRoomSetting = () => {
+	return publicGet('/api/app/home/live-room-setting')
+}
\ No newline at end of file

--
Gitblit v1.7.1