فهرست منبع

fix: 视频帮办模块调整

zhangying 1 هفته پیش
والد
کامیت
6d23568408
22فایلهای تغییر یافته به همراه1095 افزوده شده و 419 حذف شده
  1. 593 0
      AIWork/260601-前端页面各功能实现方式与代码规范.md
  2. 0 0
      .docs/260601-后端各功能接口实现方式与代码规范分析.md
  3. 0 0
      .docs/260601-项目结构与技术框架分析.md
  4. 43 17
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/controller/SpbbFeedbackController.java
  5. 4 2
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/mapper/SpbbFeedbackMapper.java
  6. 18 0
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/mapper/xml/SpbbFeedbackMapper.xml
  7. 6 4
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/service/ISpbbFeedbackService.java
  8. 10 6
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/service/impl/SpbbFeedbackServiceImpl.java
  9. 23 11
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/vo/SpbbFeedbackVo.java
  10. 12 27
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/controller/SppbVideoController.java
  11. 7 13
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/entity/SppbVideo.java
  12. 5 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/service/ISppbVideoService.java
  13. 18 1
      jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/service/impl/SppbVideoServiceImpl.java
  14. 10 14
      jeecgboot-vue3/src/views/spbb/feedback/SpbbFeedback.api.ts
  15. 19 23
      jeecgboot-vue3/src/views/spbb/feedback/SpbbFeedback.data.ts
  16. 46 54
      jeecgboot-vue3/src/views/spbb/feedback/SpbbFeedbackList.vue
  17. 49 49
      jeecgboot-vue3/src/views/spbb/feedback/components/SpbbFeedbackForm.vue
  18. 20 9
      jeecgboot-vue3/src/views/spbb/feedback/components/SpbbFeedbackModal.vue
  19. 73 75
      jeecgboot-vue3/src/views/spbb/video/SppbVideo.data.ts
  20. 56 59
      jeecgboot-vue3/src/views/spbb/video/SppbVideoList.vue
  21. 60 42
      jeecgboot-vue3/src/views/spbb/video/components/SppbVideoForm.vue
  22. 23 12
      jeecgboot-vue3/src/views/spbb/video/components/SppbVideoModal.vue

+ 593 - 0
AIWork/260601-前端页面各功能实现方式与代码规范.md

@@ -1849,3 +1849,596 @@ async function handleSubmit() {
 | [user.data.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/system/user/user.data.ts) | 用户表单校验规则示例 |
 | [IFormSchema.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/super/online/cgform/auto/comp/factory/IFormSchema.ts) | Online 表单 Schema 基类 |
 | [useAutoForm.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/super/online/cgform/hooks/auto/useAutoForm.ts) | Online 表单唯一校验 |
+
+***
+
+## 十七、BasicTable 插槽(Slot)用法详解
+
+> 追加日期:2026-06-02
+
+### 17.1 插槽机制概述
+
+BasicTable 的插槽机制由以下核心文件协同实现:
+
+| 文件                                                                                                                                     | 作用                                                             |
+|----------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|
+| [BasicTable.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/BasicTable.vue)              | 主组件,模板中定义插槽透传逻辑                                                |
+| [useTableHeader.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/hooks/useTableHeader.ts)  | 处理表头区域插槽(tableTitle, toolbar, headerTop, tableTop, alertAfter) |
+| [useTableForm.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/hooks/useTableForm.ts)      | 处理搜索表单插槽(form-xxx)                                             |
+| [TableHeader.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/components/TableHeader.vue) | 表头区域布局组件,渲染 headerTop/tableTitle/toolbar/tableTop/alertAfter   |
+
+**插槽分类流程
+**([BasicTable.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/BasicTable.vue)
+中 `slotNamesGroup` 计算属性):
+
+```
+用户在 <BasicTable> 中定义的插槽
+        |
+        v
+slotNamesGroup 计算属性自动分类
+        |
+   +----+----+
+   |         |
+   v         v
+native组   custom组   + 单独处理的插槽
+   |         |              |
+   v         v              v
+直接透传   列级自定义    bodyCell: 优先匹配 slotsBak.customRender
+给 antd     插槽渲染      form-xxx: 去掉前缀后传给 BasicForm
+<Table>                  tableTitle/toolbar/headerTop/tableTop/alertAfter:
+                         由 useTableHeader 传给 TableHeader 组件
+                         expandedRowRender/summary:
+                         直接在 <Table> 模板中使用
+```
+
+### 17.2 完整插槽列表
+
+#### 第一类:表头区域插槽
+
+##### 1. `tableTitle` — 表格标题/操作按钮区域
+
+- **位置**:表格标题栏左侧
+- **用途**:放置新增、导出、导入、批量操作等按钮,**最常用的插槽**
+- **作用域参数**:无
+
+```vue
+
+<template #tableTitle>
+  <a-button type="primary" preIcon="ant-design:plus-outlined" @click="handleCreate">新增</a-button>
+  <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls">导出</a-button>
+  <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
+  <a-dropdown v-if="selectedRowKeys.length > 0">
+    <template #overlay>
+      <a-menu>
+        <a-menu-item key="1" @click="batchHandleDelete">
+          <Icon icon="ant-design:delete-outlined"></Icon>
+          删除
+        </a-menu-item>
+      </a-menu>
+    </template>
+    <a-button>批量操作
+      <Icon icon="mdi:chevron-down"></Icon>
+    </a-button>
+  </a-dropdown>
+</template>
+```
+
+**源码实现
+**:[useTableHeader.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/hooks/useTableHeader.ts)
+将 `tableTitle` 插槽通过 `h()`
+函数传递给 [TableHeader.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/components/TableHeader.vue)
+,TableHeader 中将其渲染在左侧 `__tableTitle` 区域。当未使用此插槽但配置了 `title` prop 时,会渲染默认的 `TableTitle` 组件。
+
+##### 2. `toolbar` — 工具栏区域
+
+- **位置**:表格标题栏右侧,与 TableSetting(列配置按钮)同行
+- **用途**:放置工具按钮,如"获取表单数据"、"切换自适应高度"等
+- **作用域参数**:无
+
+```vue
+
+<template #toolbar>
+  <a-button type="primary" @click="getFormValues">获取表单数据</a-button>
+</template>
+```
+
+**源码实现**:TableHeader 中 `toolbar` 插槽渲染在右侧 `__toolbar` 区域,后面紧跟 `TableSetting` 组件。当 `toolbar` 插槽和
+`showTableSetting` 同时存在时,中间会自动添加竖线分隔符 `Divider`。
+
+##### 3. `headerTop` — 表头顶部区域
+
+- **位置**:表格标题栏上方,独立一行
+- **用途**:放置提示信息、自定义选中状态显示等
+- **作用域参数**:无
+
+```vue
+
+<template #headerTop>
+  <a-alert type="info" show-icon>
+    <template #message>
+      <template v-if="checkedKeys.length > 0">
+        <span>已选中{{ checkedKeys.length }}条记录(可跨页)</span>
+        <a-button type="link" @click="checkedKeys = []" size="small">清空</a-button>
+      </template>
+      <template v-else>
+        <span>未选中任何项目</span>
+      </template>
+    </template>
+  </a-alert>
+</template>
+```
+
+**源码实现**:TableHeader 中 `headerTop` 插槽在最顶部渲染,外层包裹 `margin: 5px` 的 div。
+
+##### 4. `tableTop` — 表头下方区域(替代默认选中提示)
+
+- **位置**:标题栏下方,默认是选中行数的 alert 提示
+- **用途**:自定义选中提示区域,或用空内容隐藏默认提示
+- **作用域参数**:无
+
+```vue
+<!-- 用法1:隐藏默认选中提示 -->
+<template #tableTop><span></span></template>
+
+<!-- 用法2:自定义选中提示内容 -->
+<template #tableTop>
+  <a-alert type="info" show-icon>
+    <template #message>自定义提示内容</template>
+  </a-alert>
+</template>
+```
+
+**源码实现**:TableHeader 中 `tableTop` 插槽有默认内容——一个 `<a-alert>` 显示"已选中 X 条记录"和"清空"链接。使用此插槽会*
+*完全替换**默认提示。
+
+##### 5. `alertAfter` — 选中提示后方区域
+
+- **位置**:紧跟在默认选中提示 alert 之后
+- **用途**:在选中提示后追加自定义内容
+- **作用域参数**:无
+
+**源码实现**:`alertAfter` 插槽嵌套在 `tableTop` 默认内容的 `<a-alert>` 内部,仅在选中行数 > 0 时显示。项目暂未发现实际使用案例。
+
+#### 第二类:表格内容区域插槽
+
+##### 6. `bodyCell` — 单元格内容(antdv3.x 兼容)
+
+- **位置**:表格所有单元格
+- **用途**:统一拦截所有单元格渲染,根据 `column.key` 判断是否自定义渲染
+- **作用域参数**:`{ column, record, text, index, renderIndex }`
+
+```vue
+
+<template #bodyCell="{ column, record }">
+  <Avatar v-if="column.key === 'avatar'" :size="60" :src="record.avatar"/>
+</template>
+```
+
+**重要机制
+**:[BasicTable.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/BasicTable.vue)
+中 `bodyCell` 插槽有特殊逻辑:
+
+```vue
+
+<template #bodyCell="data">
+  <template v-if="data.column?.slotsBak?.customRender">
+    <!-- 如果列配置了 slotsBak.customRender,优先使用列级自定义插槽 -->
+    <slot :name="data.column.slotsBak.customRender" v-bind="data || {}"></slot>
+  </template>
+  <template v-else>
+    <!-- 否则使用 bodyCell 插槽 -->
+    <slot name="bodyCell" v-bind="data || {}"></slot>
+  </template>
+</template>
+```
+
+即:如果列配置了 `slots.customRender`,会优先使用列级命名插槽;否则走 `bodyCell` 全局插槽。
+
+##### 7. `expandedRowRender` — 展开行内容
+
+- **位置**:点击行展开图标后显示的子内容区域
+- **用途**:展示子表、详情信息、嵌套表格等
+- **作用域参数**:`{ record, index, indent, expanded }`
+
+```vue
+<!-- 简单用法 -->
+<template #expandedRowRender="{ record }">
+  <span>No: {{ record.no }}</span>
+</template>
+
+<!-- 复杂用法 - 嵌套子表 -->
+<template #expandedRowRender="{ record }">
+  <a-tabs v-model:activeKey="innerSubTable.tabIndex">
+    <a-tab-pane v-for="(item, index) in innerSubTable.tabNav" :tab="item.tableTxt" :key="index + ''">
+      <OnlCgformInnerSubTable :subTableId="item.id" :mTableSelectedRcordId="expandedRowKeys[0]"/>
+    </a-tab-pane>
+  </a-tabs>
+</template>
+```
+
+##### 8. `summary` — 表尾合计区域
+
+- **位置**:表格底部合计行
+- **用途**:自定义合计行渲染,覆盖默认的 TableSummary 组件
+- **作用域参数**:`{ pageData }`(当前页数据)
+
+```vue
+
+<template #summary="{ pageData }">
+  <TableSummary fixed>
+    <TableSummaryRow>
+      <TableSummaryCell :index="0">合计</TableSummaryCell>
+      <TableSummaryCell :index="1">{{ totalAmount }}</TableSummaryCell>
+    </TableSummaryRow>
+  </TableSummary>
+</template>
+```
+
+**生效条件**:此插槽仅在 `showSummaryRef` 为 true(即配置了 `summaryFunc` 或 `summaryData` 且有数据)且 `showSummary` prop
+为 false 时生效。默认情况下使用内置 TableSummary 组件渲染。
+
+##### 9. `headerCell` — 表头单元格
+
+- **位置**:表头单元格
+- **用途**:自定义表头单元格渲染
+- **作用域参数**:`{ column, title }`
+
+**特殊处理**:当列是自定义选择列时,会渲染 `CustomSelectHeader` 而非用户自定义内容(解决全选框不显示的问题)。
+
+#### 第三类:列级自定义插槽
+
+##### 10. 列内容自定义插槽(`slots.customRender`)
+
+- **定义方式**:在列配置中设置 `slots: { customRender: 'slotName' }`
+- **作用域参数**:`{ text, record, column, index, renderIndex }`
+
+```typescript
+// 列配置
+const columns: BasicColumn[] = [
+    {title: 'ID', dataIndex: 'id', slots: {customRender: 'id'}},
+    {title: '编号', dataIndex: 'no', slots: {customRender: 'no'}},
+    {title: '图片', dataIndex: 'imgArr', slots: {customRender: 'img'}},
+];
+```
+
+```vue
+<!-- 对应的插槽 -->
+<template #id="{ record }">ID: {{ record.id }}</template>
+<template #no="{ record }">
+  <Tag color="green">{{ record.no }}</Tag>
+</template>
+<template #img="{ text }">
+  <TableImg :size="60" :simpleShow="true" :imgList="text"/>
+</template>
+```
+
+**Online 表单中的典型用法**:
+
+```vue
+<!-- 文件下载插槽 -->
+<template #fileSlot="{ text, record, column }">
+  <span v-if="!text">无文件</span>
+  <a-button v-else :ghost="true" type="primary" size="small" @click="downloadRowFile(text, record, column, ID)">下载
+  </a-button>
+</template>
+
+<!-- 图片预览插槽 -->
+<template #imgSlot="{ text }">
+  <span v-if="!text">无图片</span>
+  <img v-else :src="getImgView(text)" alt="图片不存在" class="online-cell-image"/>
+</template>
+
+<!-- 富文本插槽 -->
+<template #htmlSlot="{ text, column, record }">
+  <template v-if="column.fieldHref">
+    <a v-html="text" @click="handleClickFieldHref(column.fieldHref, record)"></a>
+  </template>
+  <div v-else v-html="text"></div>
+</template>
+
+<!-- 省市区插槽 -->
+<template #pcaSlot="{ text, column }">
+  <div :title="getPcaText(text, column)">{{ getPcaText(text, column) }}</div>
+</template>
+
+<!-- 日期格式化插槽 -->
+<template #dateSlot="{ text, column }">
+  <span>{{ getFormatDate(text, column) }}</span>
+</template>
+```
+
+##### 11. 列标题自定义插槽(`slots.title`)
+
+- **定义方式**:在列配置中设置 `slots: { title: 'slotName' }`
+- **作用域参数**:无
+
+```typescript
+// 列配置
+{
+    dataIndex: 'name', slots
+:
+    {
+        title: 'customTitle'
+    }
+}
+{
+    dataIndex: 'address', slots
+:
+    {
+        title: 'customAddress'
+    }
+}
+```
+
+```vue
+
+<template #customTitle>
+  <span>姓名 <BasicHelp class="ml-2" text="姓名"/></span>
+</template>
+<template #customAddress>
+  地址
+  <FormOutlined class="ml-2"/>
+</template>
+```
+
+##### 12. `action` — 操作列插槽(最常用的列级插槽)
+
+- **定义方式**:通过 `actionColumn` 配置自动生成,内部设置 `slots: { customRender: 'action' }`
+- **作用域参数**:`{ record, index }`
+
+```vue
+
+<template #action="{ record }">
+  <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
+</template>
+```
+
+#### 第四类:搜索表单插槽
+
+##### 13. `form-xxx` — 搜索表单字段自定义插槽
+
+- **定义方式**:在 FormSchema 中配置 `slot: 'slotName'`,使用时插槽名为 `form-slotName`
+- **作用域参数**:`{ model, field, values }`(model 是表单数据对象,field 是字段名)
+
+```typescript
+// FormSchema 配置
+{
+    field: 'email', component
+:
+    'JInput', slot
+:
+    'email'
+}
+{
+    field: 'age', component
+:
+    'JInput', slot
+:
+    'age'
+}
+```
+
+```vue
+<!-- 邮箱自定义搜索 -->
+<template #form-email="{ model, field }">
+  <a-input placeholder="请输入邮箱" v-model:value="model[field]" addon-before="邮箱:" addon-after=".com"></a-input>
+</template>
+
+<!-- 年龄范围搜索 -->
+<template #form-age="{ model, field }">
+  <a-input placeholder="最小年龄" type="ge" v-model:value="min" style="width: calc(50% - 15px)"
+           @change="ageChange(model, field)"></a-input>
+  <span>~</span>
+  <a-input placeholder="最大年龄" type="le" v-model:value="max" style="width: calc(50% - 15px)"
+           @change="ageChange(model, field)"></a-input>
+</template>
+```
+
+**机制说明
+**:[useTableForm.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/hooks/useTableForm.ts)
+中,`getFormSlotKeys` 计算属性会自动识别所有以 `form-` 开头的插槽,`replaceFormSlotKey` 函数将 `form-` 前缀去掉后映射到
+BasicForm 的对应插槽。
+
+#### 第五类:Ant Design 原生插槽透传
+
+`slotNamesGroup` 计算属性会将非 `bodyCell` 且非列级自定义的插槽归入 `native` 组,直接透传给底层 `<Table>` 组件。因此 Ant
+Design Vue Table 支持的所有原生插槽都可以直接使用,包括但不限于:
+
+| 原生插槽               | 作用域参数                            | 用途       |
+|--------------------|----------------------------------|----------|
+| `headerCell`       | `{ column, title }`              | 自定义表头单元格 |
+| `customFilterIcon` | `{ filtered }`                   | 自定义筛选图标  |
+| `expandIcon`       | `{ expanded, onExpand, record }` | 自定义展开图标  |
+| `emptyText`        | 无                                | 空数据提示    |
+
+### 17.3 插槽分类汇总表
+
+| 插槽名                 | 类别    | 作用域参数                                 | 典型放置组件                                      | 使用频率 |
+|---------------------|-------|---------------------------------------|---------------------------------------------|------|
+| `tableTitle`        | 表头区域  | 无                                     | a-button, a-dropdown, j-upload-button, Icon | 极高   |
+| `toolbar`           | 表头区域  | 无                                     | a-button                                    | 中    |
+| `headerTop`         | 表头区域  | 无                                     | a-alert                                     | 低    |
+| `tableTop`          | 表头区域  | 无                                     | 自定义内容/空span(隐藏默认提示)                         | 低    |
+| `alertAfter`        | 表头区域  | 无                                     | 自定义内容                                       | 极低   |
+| `bodyCell`          | 表格内容  | `{ column, record, text, index }`     | Avatar, Tag, 任意组件                           | 中    |
+| `expandedRowRender` | 表格内容  | `{ record, index, indent, expanded }` | a-tabs, 子表格, 详情面板                           | 中    |
+| `summary`           | 表格内容  | `{ pageData }`                        | TableSummary                                | 低    |
+| `headerCell`        | 表格内容  | `{ column, title }`                   | 自定义表头                                       | 低    |
+| `action`            | 列级自定义 | `{ record, index }`                   | TableAction                                 | 极高   |
+| 自定义列插槽              | 列级自定义 | `{ text, record, column, index }`     | Tag, TableImg, a-button, img                | 高    |
+| 自定义标题插槽             | 列级自定义 | 无                                     | BasicHelp, Icon, 自定义标题                      | 低    |
+| `form-xxx`          | 搜索表单  | `{ model, field, values }`            | a-input, 自定义表单控件                            | 中    |
+| Ant原生插槽             | 原生透传  | 视具体插槽而定                               | 视具体插槽而定                                     | 低    |
+
+### 17.4 插槽中可放置的前端组件
+
+BasicTable 的插槽本质上是 Vue 的标准插槽机制,可以放置**任何合法的 Vue 组件或 HTML 内容**。项目中实际使用的组件包括:
+
+#### Ant Design Vue 组件
+
+| 组件                                      | 常用插槽                        | 用途        |
+|-----------------------------------------|-----------------------------|-----------|
+| `a-button`                              | tableTitle, toolbar, action | 操作按钮(最常见) |
+| `a-dropdown` / `a-menu` / `a-menu-item` | tableTitle                  | 下拉菜单/批量操作 |
+| `a-alert`                               | headerTop, tableTop         | 提示信息      |
+| `a-input`                               | form-xxx                    | 输入框(表单插槽) |
+| `a-tabs` / `a-tab-pane`                 | expandedRowRender           | 标签页(展开行)  |
+| `Tag`                                   | 列自定义插槽                      | 标签展示      |
+| `Avatar`                                | bodyCell                    | 头像展示      |
+| `a-divider`                             | tableTitle                  | 分隔线       |
+
+#### 项目自定义组件
+
+| 组件                       | 常用插槽                | 用途                        |
+|--------------------------|---------------------|---------------------------|
+| `TableAction`            | action              | 操作列组件(配合 action 插槽使用,最核心) |
+| `TableImg`               | 列自定义插槽              | 图片展示组件                    |
+| `BasicHelp`              | 列标题插槽               | 帮助提示组件                    |
+| `Icon`                   | tableTitle, toolbar | 图标组件                      |
+| `j-upload-button`        | tableTitle          | 上传按钮                      |
+| `OnlineSuperQuery`       | tableTitle          | 高级查询组件                    |
+| `OnlCgformInnerSubTable` | expandedRowRender   | 子表组件                      |
+| `import-excel-progress`  | tableTitle          | 导入进度组件                    |
+
+#### 原生 HTML
+
+- `span`, `div`, `img`, `a` 等基础标签
+- `v-html` 渲染富文本内容
+
+#### 任意自定义 Vue 组件
+
+只要是合法的 Vue 组件,都可以放入插槽中使用。
+
+### 17.5 插槽使用代码规范
+
+#### 规范一:操作按钮统一放在 `tableTitle` 插槽
+
+```vue
+<!-- ✅ 推荐 -->
+<BasicTable @register="registerTable" :rowSelection="rowSelection">
+  <template #tableTitle>
+    <a-button type="primary" v-auth="'xxx:add'" preIcon="ant-design:plus-outlined" @click="handleAdd">新增</a-button>
+    <a-button type="primary" v-auth="'xxx:export'" preIcon="ant-design:export-outlined" @click="onExportXls">导出
+    </a-button>
+    <j-upload-button type="primary" v-auth="'xxx:import'" preIcon="ant-design:import-outlined" @click="onImportXls">
+      导入
+    </j-upload-button>
+  </template>
+</BasicTable>
+
+<!-- ❌ 不推荐:操作按钮放在其他位置 -->
+```
+
+#### 规范二:操作列统一使用 `action` 插槽 + `TableAction` 组件
+
+```vue
+<!-- ✅ 推荐 -->
+<template #action="{ record }">
+  <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
+</template>
+
+<!-- ❌ 不推荐:在 action 插槽中直接写按钮 -->
+<template #action="{ record }">
+  <a-button @click="handleEdit(record)">编辑</a-button>
+  <a-button @click="handleDelete(record)">删除</a-button>
+</template>
+```
+
+#### 规范三:列自定义渲染优先使用 `slots.customRender` 而非 `bodyCell`
+
+```typescript
+// ✅ 推荐:使用列级插槽,语义清晰
+{
+    title: '状态', dataIndex
+:
+    'status', slots
+:
+    {
+        customRender: 'status'
+    }
+}
+// 对应模板: <template #status="{ record }">...</template>
+
+// ❌ 不推荐:使用 bodyCell 全局拦截,所有列都在一个插槽中判断
+// <template #bodyCell="{ column, record }">
+//   <template v-if="column.key === 'status'">...</template>
+//   <template v-if="column.key === 'type'">...</template>
+// </template>
+```
+
+#### 规范四:搜索表单自定义控件使用 `form-xxx` 插槽
+
+```typescript
+// data.ts 中定义
+{
+    field: 'age', component
+:
+    'JInput', slot
+:
+    'age'
+}
+```
+
+```vue
+<!-- 列表页中使用 -->
+<template #form-age="{ model, field }">
+  <a-input v-model:value="model[field]" placeholder="请输入年龄"/>
+</template>
+```
+
+#### 规范五:隐藏默认选中提示时使用空 `tableTop` 插槽
+
+```vue
+<!-- 隐藏默认的"已选中X条记录"提示 -->
+<template #tableTop><span></span></template>
+```
+
+#### 规范六:批量操作按钮使用 `a-dropdown` 并条件显示
+
+```vue
+
+<template #tableTitle>
+  <!-- 其他按钮... -->
+  <a-dropdown v-if="selectedRowKeys.length > 0">
+    <template #overlay>
+      <a-menu>
+        <a-menu-item key="1" @click="batchHandleDelete">
+          <Icon icon="ant-design:delete-outlined"></Icon>
+          删除
+        </a-menu-item>
+      </a-menu>
+    </template>
+    <a-button>批量操作
+      <Icon icon="mdi:chevron-down"></Icon>
+    </a-button>
+  </a-dropdown>
+</template>
+```
+
+#### 规范七:按钮权限控制
+
+```vue
+<!-- 使用 v-auth 指令 -->
+<a-button type="primary" v-auth="'spbb:sppb_video:add'" @click="handleAdd">新增</a-button>
+
+<!-- 在 TableAction 中使用 auth 属性 -->
+function getTableAction(record): ActionItem[] {
+return [
+{ label: '编辑', onClick: handleEdit.bind(null, record), auth: 'spbb:sppb_video:edit' },
+];
+}
+```
+
+### 17.6 关键源码索引(BasicTable 插槽)
+
+| 文件                                                                                                                                                                       | 作用                                                         |
+|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------|
+| [BasicTable.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/BasicTable.vue)                                                | 主组件,定义插槽透传逻辑和 slotNamesGroup 分类                            |
+| [useTableHeader.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/hooks/useTableHeader.ts)                                    | 处理表头区域插槽传递给 TableHeader                                    |
+| [useTableForm.ts](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/hooks/useTableForm.ts)                                        | 处理 form-xxx 插槽的识别和前缀替换                                     |
+| [TableHeader.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/components/Table/src/components/TableHeader.vue)                                   | 表头布局组件,渲染 headerTop/tableTitle/toolbar/tableTop/alertAfter |
+| [CustomerCell.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/demo/table/CustomerCell.vue)                                                | 列自定义插槽 + bodyCell 使用示例                                     |
+| [ExpandTable.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/demo/table/ExpandTable.vue)                                                  | expandedRowRender 使用示例                                     |
+| [FormTable.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/demo/table/FormTable.vue)                                                      | headerTop/toolbar/form-xxx 使用示例                            |
+| [BasicTableDemoAjax.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/demo/document/table/BasicTableDemoAjax.vue)                           | form-email 自定义搜索示例                                         |
+| [OnlCgformInnerTableList.vue](file:///d:/Code/Project/湛江人社/code/zjrs-jeecgBoot/jeecgboot-vue3/src/views/super/online/cgform/auto/innerTable/OnlCgformInnerTableList.vue) | expandedRowRender + Online 列插槽综合示例                         |

AIWork/260601-后端各功能接口实现方式与代码规范分析.md → .docs/260601-后端各功能接口实现方式与代码规范分析.md


AIWork/260601-项目结构与技术框架分析.md → .docs/260601-项目结构与技术框架分析.md


+ 43 - 17
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/controller/SpbbFeedbackController.java

@@ -1,28 +1,34 @@
 package org.jeecg.modules.zjrs.spbb.feedback.controller;
 
-import java.util.Arrays;
-
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
+import org.jeecg.common.system.vo.LoginUser;
+import org.jeecg.common.util.oConvertUtils;
 import org.jeecg.modules.zjrs.spbb.feedback.entity.SpbbFeedback;
 import org.jeecg.modules.zjrs.spbb.feedback.service.ISpbbFeedbackService;
 import org.jeecg.modules.zjrs.spbb.feedback.vo.SpbbFeedbackVo;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import lombok.extern.slf4j.Slf4j;
-
-import org.jeecg.common.system.base.controller.JeecgController;
+import org.jeecgframework.poi.excel.def.NormalExcelConstants;
+import org.jeecgframework.poi.excel.entity.ExportParams;
+import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
+import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Operation;
-import org.jeecg.common.aspect.annotation.AutoLog;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
+
+import java.util.Arrays;
+import java.util.List;
  /**
  * @Description: 视频帮办-反馈信息
  * @Author: jeecg-boot
@@ -49,7 +55,6 @@ public class SpbbFeedbackController extends JeecgController<SpbbFeedback, ISpbbF
 	//@AutoLog(value = "视频帮办-反馈信息-分页列表查询")
 	@Operation(summary="视频帮办-反馈信息-分页列表查询")
 	@GetMapping(value = "/list")
-	//update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】分页列表关联视频表查询-----------
 	public Result<IPage<SpbbFeedbackVo>> queryPageList(SpbbFeedback spbbFeedback,
 								   @RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
 								   @RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
@@ -60,8 +65,7 @@ public class SpbbFeedbackController extends JeecgController<SpbbFeedback, ISpbbF
 		IPage<SpbbFeedbackVo> pageList = spbbFeedbackService.queryPageListWithVideo(page, queryWrapper);
 		return Result.OK(pageList);
 	}
-	//update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】分页列表关联视频表查询-----------
-	
+
 	/**
 	 *   添加
 	 *
@@ -149,7 +153,29 @@ public class SpbbFeedbackController extends JeecgController<SpbbFeedback, ISpbbF
     @RequiresPermissions("spbb:spbb_feedback:exportXls")
     @RequestMapping(value = "/exportXls")
     public ModelAndView exportXls(HttpServletRequest request, SpbbFeedback spbbFeedback) {
-        return super.exportXls(request, spbbFeedback, SpbbFeedback.class, "视频帮办-反馈信息");
+        QueryWrapper<SpbbFeedback> queryWrapper = QueryGenerator.initQueryWrapper(spbbFeedback, request.getParameterMap());
+        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+
+        String selections = request.getParameter("selections");
+        if (oConvertUtils.isNotEmpty(selections)) {
+            List<String> selectionList = Arrays.asList(selections.split(","));
+            queryWrapper.in("id", selectionList);
+        }
+
+        List<SpbbFeedbackVo> exportList = spbbFeedbackService.queryListWithVideo(queryWrapper);
+
+        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
+        mv.addObject(NormalExcelConstants.FILE_NAME, "视频帮办-反馈信息");
+        mv.addObject(NormalExcelConstants.CLASS, SpbbFeedbackVo.class);
+        ExportParams exportParams = new ExportParams("视频帮办-反馈信息报表", "导出人:" + sysUser.getRealname(), "视频帮办-反馈信息", ExcelType.XSSF);
+        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
+        mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
+
+        String exportFields = request.getParameter(NormalExcelConstants.EXPORT_FIELDS);
+        if (oConvertUtils.isNotEmpty(exportFields)) {
+            mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
+        }
+        return mv;
     }
 
     /**

+ 4 - 2
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/mapper/SpbbFeedbackMapper.java

@@ -1,11 +1,11 @@
 package org.jeecg.modules.zjrs.spbb.feedback.mapper;
 
-import org.jeecg.modules.zjrs.spbb.feedback.entity.SpbbFeedback;
-import org.jeecg.modules.zjrs.spbb.feedback.vo.SpbbFeedbackVo;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Param;
+import org.jeecg.modules.zjrs.spbb.feedback.entity.SpbbFeedback;
+import org.jeecg.modules.zjrs.spbb.feedback.vo.SpbbFeedbackVo;
 
 import java.util.List;
 
@@ -18,4 +18,6 @@ import java.util.List;
 public interface SpbbFeedbackMapper extends BaseMapper<SpbbFeedback> {
 
     IPage<SpbbFeedbackVo> queryPageListWithVideo(Page<SpbbFeedbackVo> page, @Param("ew") com.baomidou.mybatisplus.core.conditions.Wrapper<SpbbFeedback> queryWrapper);
+
+    List<SpbbFeedbackVo> queryListWithVideo(@Param("ew") com.baomidou.mybatisplus.core.conditions.Wrapper<SpbbFeedback> queryWrapper);
 }

+ 18 - 0
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/mapper/xml/SpbbFeedbackMapper.xml

@@ -16,6 +16,24 @@
             f.update_time,
             f.sys_org_code,
             v.title AS video_title
+        FROM spbb_feedback f
+                 LEFT JOIN sppb_video v ON f.video_data_id = v.id
+            ${ew.customSqlSegment}
+    </select>
+
+    <select id="queryListWithVideo" resultType="org.jeecg.modules.zjrs.spbb.feedback.vo.SpbbFeedbackVo">
+        SELECT f.id,
+               f.video_data_id,
+               f.feedback_type,
+               f.video_category,
+               f.feedback_content,
+               f.contact_info,
+               f.create_by,
+               f.create_time,
+               f.update_by,
+               f.update_time,
+               f.sys_org_code,
+               v.title AS video_title
         FROM spbb_feedback f
         LEFT JOIN sppb_video v ON f.video_data_id = v.id
         ${ew.customSqlSegment}

+ 6 - 4
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/service/ISpbbFeedbackService.java

@@ -1,11 +1,13 @@
 package org.jeecg.modules.zjrs.spbb.feedback.service;
 
-import org.jeecg.modules.zjrs.spbb.feedback.entity.SpbbFeedback;
-import org.jeecg.modules.zjrs.spbb.feedback.vo.SpbbFeedbackVo;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.zjrs.spbb.feedback.entity.SpbbFeedback;
+import org.jeecg.modules.zjrs.spbb.feedback.vo.SpbbFeedbackVo;
+
+import java.util.List;
 
 /**
  * @Description: 视频帮办-反馈信息
@@ -15,7 +17,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ISpbbFeedbackService extends IService<SpbbFeedback> {
 
-    //update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】分页列表关联视频表查询-----------
     IPage<SpbbFeedbackVo> queryPageListWithVideo(Page<SpbbFeedbackVo> page, Wrapper<SpbbFeedback> queryWrapper);
-    //update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】分页列表关联视频表查询-----------
+
+    List<SpbbFeedbackVo> queryListWithVideo(Wrapper<SpbbFeedback> queryWrapper);
 }

+ 10 - 6
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/service/impl/SpbbFeedbackServiceImpl.java

@@ -1,15 +1,16 @@
 package org.jeecg.modules.zjrs.spbb.feedback.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.jeecg.modules.zjrs.spbb.feedback.entity.SpbbFeedback;
 import org.jeecg.modules.zjrs.spbb.feedback.mapper.SpbbFeedbackMapper;
 import org.jeecg.modules.zjrs.spbb.feedback.service.ISpbbFeedbackService;
 import org.jeecg.modules.zjrs.spbb.feedback.vo.SpbbFeedbackVo;
 import org.springframework.stereotype.Service;
 
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.List;
 
 /**
  * @Description: 视频帮办-反馈信息
@@ -20,10 +21,13 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 @Service
 public class SpbbFeedbackServiceImpl extends ServiceImpl<SpbbFeedbackMapper, SpbbFeedback> implements ISpbbFeedbackService {
 
-    //update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】分页列表关联视频表查询-----------
     @Override
     public IPage<SpbbFeedbackVo> queryPageListWithVideo(Page<SpbbFeedbackVo> page, Wrapper<SpbbFeedback> queryWrapper) {
         return baseMapper.queryPageListWithVideo(page, queryWrapper);
     }
-    //update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】分页列表关联视频表查询-----------
+
+    @Override
+    public List<SpbbFeedbackVo> queryListWithVideo(Wrapper<SpbbFeedback> queryWrapper) {
+        return baseMapper.queryListWithVideo(queryWrapper);
+    }
 }

+ 23 - 11
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/feedback/vo/SpbbFeedbackVo.java

@@ -1,11 +1,12 @@
 package org.jeecg.modules.zjrs.spbb.feedback.vo;
 
-import lombok.Data;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import org.springframework.format.annotation.DateTimeFormat;
 import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
 
 /**
  * @Description: 视频帮办-反馈信息(关联视频标题)
@@ -24,16 +25,30 @@ public class SpbbFeedbackVo {
 	/**视频id*/
     @Schema(description = "视频id")
     private java.lang.String videoDataId;
-	/**反馈类型*/
-    @Schema(description = "反馈类型")
-    private java.lang.String feedbackType;
-	/**视频类型(业务类型)*/
+    /**
+     * 视频标题(关联sppb_video表)
+     */
+    @Excel(name = "视频标题", width = 15)
+    @Schema(description = "视频标题")
+    private java.lang.String videoTitle;
+    /**
+     * 视频类型(业务类型)
+     */
+    @Excel(name = "视频类型", width = 15)
     @Schema(description = "视频类型(业务类型)")
     private java.lang.String videoCategory;
-	/**反馈内容*/
+    /**
+     * 反馈类型
+     */
+    @Excel(name = "反馈类型", width = 15)
+    @Schema(description = "反馈类型")
+    private java.lang.String feedbackType;
+    /**反馈内容*/
+	@Excel(name = "反馈内容", width = 15)
     @Schema(description = "反馈内容")
     private java.lang.String feedbackContent;
-	/**联系方式*/
+    /**联系方式*/
+	@Excel(name = "联系方式", width = 15)
     @Schema(description = "联系方式")
     private java.lang.String contactInfo;
 	/**创建人*/
@@ -55,7 +70,4 @@ public class SpbbFeedbackVo {
 	/**所属部门*/
     @Schema(description = "所属部门")
     private java.lang.String sysOrgCode;
-	/**视频标题(关联sppb_video表)*/
-    @Schema(description = "视频标题")
-    private java.lang.String videoTitle;
 }

+ 12 - 27
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/controller/SppbVideoController.java

@@ -1,32 +1,27 @@
 package org.jeecg.modules.zjrs.spbb.video.controller;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.jeecg.common.api.vo.Result;
+import org.jeecg.common.aspect.annotation.AutoLog;
+import org.jeecg.common.system.base.controller.JeecgController;
 import org.jeecg.common.system.query.QueryGenerator;
 import org.jeecg.modules.zjrs.spbb.video.entity.SppbVideo;
 import org.jeecg.modules.zjrs.spbb.video.service.ISppbVideoService;
-
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import lombok.extern.slf4j.Slf4j;
-
-import org.jeecg.common.system.base.controller.JeecgController;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.servlet.ModelAndView;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Operation;
-import org.jeecg.common.aspect.annotation.AutoLog;
-import org.apache.shiro.authz.annotation.RequiresPermissions;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
-import java.util.HashMap;
  /**
  * @Description: 视频帮办-视频管理
  * @Author: jeecg-boot
@@ -168,7 +163,6 @@ public class SppbVideoController extends JeecgController<SppbVideo, ISppbVideoSe
         return super.importExcel(request, response, SppbVideo.class);
     }
 
-    //update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉列表接口-----------
     /**
      * 视频下拉列表(供反馈信息等模块选择视频使用)
      *
@@ -177,17 +171,8 @@ public class SppbVideoController extends JeecgController<SppbVideo, ISppbVideoSe
     @Operation(summary="视频帮办-视频下拉列表")
     @GetMapping(value = "/listForSelect")
     public Result<List<Map<String, String>>> listForSelect() {
-        QueryWrapper<SppbVideo> queryWrapper = new QueryWrapper<>();
-        queryWrapper.select("id", "title");
-        List<SppbVideo> videoList = sppbVideoService.list(queryWrapper);
-        List<Map<String, String>> resultList = videoList.stream().map(video -> {
-            Map<String, String> map = new HashMap<>();
-            map.put("value", video.getId());
-            map.put("label", video.getTitle());
-            return map;
-        }).collect(Collectors.toList());
+        List<Map<String, String>> resultList = sppbVideoService.listForSelect();
         return Result.OK(resultList);
     }
-    //update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉列表接口-----------
 
 }

+ 7 - 13
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/entity/SppbVideo.java

@@ -1,23 +1,17 @@
 package org.jeecg.modules.zjrs.spbb.video.entity;
 
-import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
-import java.util.Date;
-import java.math.BigDecimal;
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.annotation.TableLogic;
-import org.jeecg.common.constant.ProvinceCityArea;
-import org.jeecg.common.util.SpringContextUtils;
-import lombok.Data;
 import com.fasterxml.jackson.annotation.JsonFormat;
-import org.springframework.format.annotation.DateTimeFormat;
-import org.jeecgframework.poi.excel.annotation.Excel;
-import org.jeecg.common.aspect.annotation.Dict;
 import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.jeecgframework.poi.excel.annotation.Excel;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
 
 /**
  * @Description: 视频帮办-视频管理
@@ -42,11 +36,11 @@ public class SppbVideo implements Serializable {
     @Schema(description = "视频标题")
     private java.lang.String title;
 	/**视频封面*/
-	@Excel(name = "视频封面", width = 15)
+//	@Excel(name = "视频封面", width = 15)
     @Schema(description = "视频封面")
     private java.lang.String cover;
 	/**视频文件*/
-	@Excel(name = "视频文件", width = 15)
+//	@Excel(name = "视频文件", width = 15)
     @Schema(description = "视频文件")
     private java.lang.String videoFile;
 	/**视频格式*/

+ 5 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/service/ISppbVideoService.java

@@ -1,7 +1,10 @@
 package org.jeecg.modules.zjrs.spbb.video.service;
 
-import org.jeecg.modules.zjrs.spbb.video.entity.SppbVideo;
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.jeecg.modules.zjrs.spbb.video.entity.SppbVideo;
+
+import java.util.List;
+import java.util.Map;
 
 /**
  * @Description: 视频帮办-视频管理
@@ -11,4 +14,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ISppbVideoService extends IService<SppbVideo> {
 
+    List<Map<String, String>> listForSelect();
 }

+ 18 - 1
jeecg-boot/jeecg-boot-module/jeecg-module-zjrs/src/main/java/org/jeecg/modules/zjrs/spbb/video/service/impl/SppbVideoServiceImpl.java

@@ -1,11 +1,16 @@
 package org.jeecg.modules.zjrs.spbb.video.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.jeecg.modules.zjrs.spbb.video.entity.SppbVideo;
 import org.jeecg.modules.zjrs.spbb.video.mapper.SppbVideoMapper;
 import org.jeecg.modules.zjrs.spbb.video.service.ISppbVideoService;
 import org.springframework.stereotype.Service;
 
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @Description: 视频帮办-视频管理
@@ -16,4 +21,16 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 @Service
 public class SppbVideoServiceImpl extends ServiceImpl<SppbVideoMapper, SppbVideo> implements ISppbVideoService {
 
+    @Override
+    public List<Map<String, String>> listForSelect() {
+        QueryWrapper<SppbVideo> queryWrapper = new QueryWrapper<>();
+        queryWrapper.select("id", "title");
+        List<SppbVideo> videoList = this.list(queryWrapper);
+        return videoList.stream().map(video -> {
+            Map<String, String> map = new HashMap<>();
+            map.put("value", video.getId());
+            map.put("label", video.getTitle());
+            return map;
+        }).collect(Collectors.toList());
+    }
 }

+ 10 - 14
jeecgboot-vue3/src/views/spbb/feedback/SpbbFeedback.api.ts

@@ -1,19 +1,17 @@
 import { defHttp } from '/@/utils/http/axios';
-import { useMessage } from "/@/hooks/web/useMessage";
+import { useMessage } from '/@/hooks/web/useMessage';
 
 const { createConfirm } = useMessage();
 
 enum Api {
   list = '/spbb/spbbFeedback/list',
-  save='/spbb/spbbFeedback/add',
-  edit='/spbb/spbbFeedback/edit',
+  save = '/spbb/spbbFeedback/add',
+  edit = '/spbb/spbbFeedback/edit',
   deleteOne = '/spbb/spbbFeedback/delete',
   deleteBatch = '/spbb/spbbFeedback/deleteBatch',
   importExcel = '/spbb/spbbFeedback/importExcel',
   exportXls = '/spbb/spbbFeedback/exportXls',
-  //update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉列表接口-----------
   videoListForSelect = '/spbb/sppbVideo/listForSelect',
-  //update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉列表接口-----------
 }
 
 /**
@@ -38,11 +36,11 @@ export const list = (params) => defHttp.get({ url: Api.list, params });
  * @param params
  * @param handleSuccess
  */
-export const deleteOne = (params,handleSuccess) => {
-  return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
+export const deleteOne = (params, handleSuccess) => {
+  return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
     handleSuccess();
   });
-}
+};
 
 /**
  * 批量删除
@@ -57,12 +55,12 @@ export const batchDelete = (params, handleSuccess) => {
     okText: '确认',
     cancelText: '取消',
     onOk: () => {
-      return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
+      return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
         handleSuccess();
       });
-    }
+    },
   });
-}
+};
 
 /**
  * 保存或者更新
@@ -72,12 +70,10 @@ export const batchDelete = (params, handleSuccess) => {
 export const saveOrUpdate = (params, isUpdate) => {
   let url = isUpdate ? Api.edit : Api.save;
   return defHttp.post({ url: url, params }, { isTransformResponse: false });
-}
+};
 
-//update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉列表接口-----------
 /**
  * 视频下拉列表
  * @param params
  */
 export const videoListForSelect = () => defHttp.get({ url: Api.videoListForSelect });
-//update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉列表接口-----------

+ 19 - 23
jeecgboot-vue3/src/views/spbb/feedback/SpbbFeedback.data.ts

@@ -1,44 +1,40 @@
-import {BasicColumn} from '/@/components/Table';
-import {FormSchema} from '/@/components/Table';
-import { rules} from '/@/utils/helper/validator';
-import { render } from '/@/utils/common/renderUtils';
-import { getWeekMonthQuarterYear } from '/@/utils';
+import { BasicColumn } from '/@/components/Table';
+
 //列表数据
 export const columns: BasicColumn[] = [
-  //update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】列表显示视频标题-----------
   {
-    title: '反馈视频',
-    align: "center",
-    dataIndex: 'videoTitle'
+    title: '反馈视频标题',
+    align: 'center',
+    dataIndex: 'videoTitle',
   },
-  //update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】列表显示视频标题-----------
   {
     title: '反馈类型',
-    align: "center",
-    dataIndex: 'feedbackType'
+    align: 'center',
+    dataIndex: 'feedbackType',
   },
   {
     title: '视频类型(业务类型)',
-    align: "center",
-    dataIndex: 'videoCategory'
+    align: 'center',
+    dataIndex: 'videoCategory',
   },
   {
     title: '反馈内容',
-    align: "center",
-    dataIndex: 'feedbackContent'
+    align: 'center',
+    dataIndex: 'feedbackContent',
   },
   {
     title: '联系方式',
-    align: "center",
-    dataIndex: 'contactInfo'
+    align: 'center',
+    dataIndex: 'contactInfo',
   },
 ];
 
 // 高级查询数据
 export const superQuerySchema = {
-  videoDataId: {title: '视频id',order: 0,view: 'text', type: 'string',},
-  feedbackType: {title: '反馈类型',order: 1,view: 'text', type: 'string',},
-  videoCategory: {title: '视频类型(业务类型)',order: 2,view: 'text', type: 'string',},
-  feedbackContent: {title: '反馈内容',order: 3,view: 'text', type: 'string',},
-  contactInfo: {title: '联系方式',order: 4,view: 'text', type: 'string',},
+  // videoDataId: {title: '视频id',order: 0,view: 'text', type: 'string',},
+  videoTitle: { title: '反馈视频标题', order: 0, view: 'text', type: 'string' },
+  feedbackType: { title: '反馈类型', order: 1, view: 'text', type: 'string' },
+  videoCategory: { title: '视频类型(业务类型)', order: 2, view: 'text', type: 'string' },
+  feedbackContent: { title: '反馈内容', order: 3, view: 'text', type: 'string' },
+  contactInfo: { title: '联系方式', order: 4, view: 'text', type: 'string' },
 };

+ 46 - 54
jeecgboot-vue3/src/views/spbb/feedback/SpbbFeedbackList.vue

@@ -1,20 +1,18 @@
-
-<template>
+<template>
   <div class="p-2">
     <!--查询区域-->
     <div class="jeecg-basic-table-form-container">
       <a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
-        <a-row :gutter="24">
-        </a-row>
+        <a-row :gutter="24"> </a-row>
       </a-form>
     </div>
     <!--引用表格-->
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <!--插槽:table标题-->
       <template #tableTitle>
-        <a-button type="primary" v-auth="'spbb:spbb_feedback:add'"  @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
-        <a-button  type="primary" v-auth="'spbb:spbb_feedback:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
-        <j-upload-button  type="primary" v-auth="'spbb:spbb_feedback:importExcel'"  preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
+        <a-button v-auth="'spbb:spbb_feedback:add'" preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd"> 新增</a-button>
+        <a-button v-auth="'spbb:spbb_feedback:exportXls'" preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
+        <!--        <j-upload-button  type="primary" v-auth="'spbb:spbb_feedback:importExcel'"  preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>-->
         <a-dropdown v-if="selectedRowKeys.length > 0">
           <template #overlay>
             <a-menu>
@@ -24,7 +22,8 @@
               </a-menu-item>
             </a-menu>
           </template>
-          <a-button v-auth="'spbb:spbb_feedback:deleteBatch'">批量操作
+          <a-button v-auth="'spbb:spbb_feedback:deleteBatch'"
+            >批量操作
             <Icon icon="mdi:chevron-down"></Icon>
           </a-button>
         </a-dropdown>
@@ -33,10 +32,9 @@
       </template>
       <!--操作栏-->
       <template #action="{ record }">
-        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
-      </template>
-      <template v-slot:bodyCell="{ column, record, index, text }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
       </template>
+      <template v-slot:bodyCell="{ column, record, index, text }"> </template>
     </BasicTable>
     <!-- 表单区域 -->
     <SpbbFeedbackModal ref="registerModal" @success="handleSuccess"></SpbbFeedbackModal>
@@ -44,20 +42,17 @@
 </template>
 
 <script lang="ts" name="spbb-spbbFeedback" setup>
-  import { ref, reactive } from 'vue';
-  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { reactive, ref } from 'vue';
+  import { BasicTable, TableAction } from '/@/components/Table';
   import { useListPage } from '/@/hooks/system/useListPage';
   import { columns, superQuerySchema } from './SpbbFeedback.data';
-  import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './SpbbFeedback.api';
-  import { downloadFile } from '/@/utils/common/renderUtils';
-  import SpbbFeedbackModal from './components/SpbbFeedbackModal.vue'
+  import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './SpbbFeedback.api';
+  import SpbbFeedbackModal from './components/SpbbFeedbackModal.vue';
   import { useUserStore } from '/@/store/modules/user';
   import { useMessage } from '/@/hooks/web/useMessage';
-   import {useModal} from '/@/components/Modal';
   import { getDateByPicker } from '/@/utils';
 
-  const fieldPickers = reactive({
-  });
+  const fieldPickers = reactive({});
 
   const formRef = ref();
   const queryParam = reactive<any>({});
@@ -71,7 +66,7 @@
       title: '视频帮办-反馈信息',
       api: list,
       columns,
-      canResize:true,
+      canResize: true,
       useSearchForm: false,
       actionColumn: {
         width: 120,
@@ -87,21 +82,22 @@
       },
     },
     exportConfig: {
-      name: "视频帮办-反馈信息",
+      name: '视频帮办-反馈信息',
       url: getExportUrl,
       params: queryParam,
     },
-	  importConfig: {
-	    url: getImportUrl,
-	    success: handleSuccess
-	  },
+    importConfig: {
+      url: getImportUrl,
+      success: handleSuccess,
+    },
   });
-  const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
+  const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
+    tableContext;
   const labelCol = reactive({
-    xs:24,
-    sm:4,
-    xl:6,
-    xxl:4
+    xs: 24,
+    sm: 4,
+    xl: 6,
+    xxl: 4,
   });
   const wrapperCol = reactive({
     xs: 24,
@@ -128,7 +124,7 @@
     registerModal.value.disableSubmit = false;
     registerModal.value.add();
   }
-  
+
   /**
    * 编辑事件
    */
@@ -136,7 +132,7 @@
     registerModal.value.disableSubmit = false;
     registerModal.value.edit(record);
   }
-   
+
   /**
    * 详情
    */
@@ -144,28 +140,28 @@
     registerModal.value.disableSubmit = true;
     registerModal.value.edit(record);
   }
-   
+
   /**
    * 删除事件
    */
   async function handleDelete(record) {
     await deleteOne({ id: record.id }, handleSuccess);
   }
-   
+
   /**
    * 批量删除事件
    */
   async function batchHandleDelete() {
     await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
   }
-   
+
   /**
    * 成功回调
    */
   function handleSuccess() {
     (selectedRowKeys.value = []) && reload();
   }
-   
+
   /**
    * 操作栏
    */
@@ -174,11 +170,11 @@
       {
         label: '编辑',
         onClick: handleEdit.bind(null, record),
-        auth: 'spbb:spbb_feedback:edit'
+        auth: 'spbb:spbb_feedback:edit',
       },
     ];
   }
-   
+
   /**
    * 下拉操作栏
    */
@@ -187,16 +183,17 @@
       {
         label: '详情',
         onClick: handleDetail.bind(null, record),
-      }, {
+      },
+      {
         label: '删除',
         popConfirm: {
           title: '是否确认删除',
           confirm: handleDelete.bind(null, record),
           placement: 'topLeft',
         },
-        auth: 'spbb:spbb_feedback:delete'
-      }
-    ]
+        auth: 'spbb:spbb_feedback:delete',
+      },
+    ];
   }
 
   /**
@@ -205,7 +202,7 @@
   function searchQuery() {
     reload();
   }
-  
+
   /**
    * 重置
    */
@@ -215,12 +212,6 @@
     //刷新数据
     reload();
   }
-  
-
-
-
-
-
 </script>
 
 <style lang="less" scoped>
@@ -231,19 +222,20 @@
       margin-bottom: 24px;
       white-space: nowrap;
     }
-    .query-group-cust{
+    .query-group-cust {
       min-width: 100px !important;
     }
-    .query-group-split-cust{
+    .query-group-split-cust {
       width: 30px;
       display: inline-block;
-      text-align: center
+      text-align: center;
     }
-    .ant-form-item:not(.ant-form-item-with-help){
+    .ant-form-item:not(.ant-form-item-with-help) {
       margin-bottom: 16px;
       height: 32px;
     }
-    :deep(.ant-picker),:deep(.ant-input-number){
+    :deep(.ant-picker),
+    :deep(.ant-input-number) {
       width: 100%;
     }
   }

+ 49 - 49
jeecgboot-vue3/src/views/spbb/feedback/components/SpbbFeedbackForm.vue

@@ -4,31 +4,37 @@
       <template #detail>
         <a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="SpbbFeedbackForm">
           <a-row>
-						<a-col :span="24">
-							<a-form-item label="反馈视频" v-bind="validateInfos.videoDataId" id="SpbbFeedbackForm-videoDataId" name="videoDataId">
-								<a-select v-model:value="formData.videoDataId" placeholder="请选择反馈视频" allow-clear :options="videoOptions" :loading="videoLoading"></a-select>
-							</a-form-item>
-						</a-col>
-						<a-col :span="24">
-							<a-form-item label="反馈类型" v-bind="validateInfos.feedbackType" id="SpbbFeedbackForm-feedbackType" name="feedbackType">
-								<a-input v-model:value="formData.feedbackType" placeholder="请输入反馈类型"  allow-clear ></a-input>
-							</a-form-item>
-						</a-col>
-						<a-col :span="24">
-							<a-form-item label="视频类型(业务类型)" v-bind="validateInfos.videoCategory" id="SpbbFeedbackForm-videoCategory" name="videoCategory">
-								<a-input v-model:value="formData.videoCategory" placeholder="请输入视频类型(业务类型)"  allow-clear ></a-input>
-							</a-form-item>
-						</a-col>
-						<a-col :span="24">
-							<a-form-item label="反馈内容" v-bind="validateInfos.feedbackContent" id="SpbbFeedbackForm-feedbackContent" name="feedbackContent">
-								<a-input v-model:value="formData.feedbackContent" placeholder="请输入反馈内容"  allow-clear ></a-input>
-							</a-form-item>
-						</a-col>
-						<a-col :span="24">
-							<a-form-item label="联系方式" v-bind="validateInfos.contactInfo" id="SpbbFeedbackForm-contactInfo" name="contactInfo">
-								<a-input v-model:value="formData.contactInfo" placeholder="请输入联系方式"  allow-clear ></a-input>
-							</a-form-item>
-						</a-col>
+            <a-col :span="24">
+              <a-form-item label="反馈视频" v-bind="validateInfos.videoDataId" id="SpbbFeedbackForm-videoDataId" name="videoDataId">
+                <a-select
+                  v-model:value="formData.videoDataId"
+                  placeholder="请选择反馈视频"
+                  allow-clear
+                  :options="videoOptions"
+                  :loading="videoLoading"
+                ></a-select>
+              </a-form-item>
+            </a-col>
+            <a-col :span="24">
+              <a-form-item label="反馈类型" v-bind="validateInfos.feedbackType" id="SpbbFeedbackForm-feedbackType" name="feedbackType">
+                <a-input v-model:value="formData.feedbackType" placeholder="请输入反馈类型" allow-clear></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="24">
+              <a-form-item label="视频类型(业务类型)" v-bind="validateInfos.videoCategory" id="SpbbFeedbackForm-videoCategory" name="videoCategory">
+                <a-input v-model:value="formData.videoCategory" placeholder="请输入视频类型(业务类型)" allow-clear></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="24">
+              <a-form-item label="反馈内容" v-bind="validateInfos.feedbackContent" id="SpbbFeedbackForm-feedbackContent" name="feedbackContent">
+                <a-input v-model:value="formData.feedbackContent" placeholder="请输入反馈内容" allow-clear></a-input>
+              </a-form-item>
+            </a-col>
+            <a-col :span="24">
+              <a-form-item label="联系方式" v-bind="validateInfos.contactInfo" id="SpbbFeedbackForm-contactInfo" name="contactInfo">
+                <a-input v-model:value="formData.contactInfo" placeholder="请输入联系方式" allow-clear></a-input>
+              </a-form-item>
+            </a-col>
           </a-row>
         </a-form>
       </template>
@@ -37,42 +43,39 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
-  import { defHttp } from '/@/utils/http/axios';
+  import { computed, defineExpose, defineProps, nextTick, onMounted, reactive, ref } from 'vue';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { getDateByPicker, getValueType } from '/@/utils';
   import { saveOrUpdate, videoListForSelect } from '../SpbbFeedback.api';
   import { Form } from 'ant-design-vue';
   import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
+
   const props = defineProps({
     formDisabled: { type: Boolean, default: false },
-    formData: { type: Object, default: () => ({})},
-    formBpm: { type: Boolean, default: true }
+    formData: { type: Object, default: () => ({}) },
+    formBpm: { type: Boolean, default: true },
   });
   const formRef = ref();
   const useForm = Form.useForm;
   const emit = defineEmits(['register', 'ok']);
   const formData = reactive<Record<string, any>>({
     id: '',
-    videoDataId: '',   
-    feedbackType: '',   
-    videoCategory: '',   
-    feedbackContent: '',   
-    contactInfo: '',   
+    videoDataId: '',
+    feedbackType: '',
+    videoCategory: '',
+    feedbackContent: '',
+    contactInfo: '',
   });
   const { createMessage } = useMessage();
   const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
   const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
   const confirmLoading = ref<boolean>(false);
   //表单验证
-  const validatorRules = reactive({
-  });
+  const validatorRules = reactive({});
   const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false });
   //日期个性化选择
-  const fieldPickers = reactive({
-  });
+  const fieldPickers = reactive({});
 
-  //update-begin---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉数据加载-----------
   const videoOptions = ref<{ value: string; label: string }[]>([]);
   const videoLoading = ref<boolean>(false);
 
@@ -90,21 +93,19 @@
   onMounted(() => {
     loadVideoOptions();
   });
-  //update-end---author:ai ---date:2026-06-01  for:【反馈视频下拉】视频下拉数据加载-----------
 
   // 表单禁用
-  const disabled = computed(()=>{
-    if(props.formBpm === true){
-      if(props.formData.disabled === false){
+  const disabled = computed(() => {
+    if (props.formBpm === true) {
+      if (props.formData.disabled === false) {
         return false;
-      }else{
+      } else {
         return true;
       }
     }
     return props.formDisabled;
   });
 
-  
   /**
    * 新增
    */
@@ -120,10 +121,10 @@
       resetFields();
       const tmpData = {};
       Object.keys(formData).forEach((key) => {
-        if(record.hasOwnProperty(key)){
-          tmpData[key] = record[key]
+        if (record.hasOwnProperty(key)) {
+          tmpData[key] = record[key];
         }
-      })
+      });
       //赋值
       Object.assign(formData, tmpData);
     });
@@ -179,7 +180,6 @@
       });
   }
 
-
   defineExpose({
     add,
     edit,

+ 20 - 9
jeecgboot-vue3/src/views/spbb/feedback/components/SpbbFeedbackModal.vue

@@ -1,18 +1,29 @@
 <template>
-  <j-modal :title="title" maxHeight="500px" :width="800" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
+  <j-modal
+    :fullscreen="true"
+    :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }"
+    :title="title"
+    :visible="visible"
+    :width="800"
+    cancelText="关闭"
+    maxHeight="500px"
+    @cancel="handleCancel"
+    @ok="handleOk"
+  >
     <SpbbFeedbackForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></SpbbFeedbackForm>
-   <template #footer>
-     <a-button @click="handleCancel">取消</a-button>
-     <a-button :class="{ 'jee-hidden': disableSubmit }" type="primary" @click="handleOk">确认</a-button>
-   </template>
+    <template #footer>
+      <a-button @click="handleCancel">取消</a-button>
+      <a-button :class="{ 'jee-hidden': disableSubmit }" type="primary" @click="handleOk">确认</a-button>
+    </template>
   </j-modal>
 </template>
 
 <script lang="ts" setup>
-  import { ref, nextTick, defineExpose } from 'vue';
-  import SpbbFeedbackForm from './SpbbFeedbackForm.vue'
+  import { defineExpose, nextTick, ref } from 'vue';
+  import SpbbFeedbackForm from './SpbbFeedbackForm.vue';
   import JModal from '/@/components/Modal/src/JModal/JModal.vue';
   import { useMessage } from '/@/hooks/web/useMessage';
+
   const { createMessage } = useMessage();
   const title = ref<string>('');
   const width = ref<number>(800);
@@ -31,7 +42,7 @@
       registerForm.value.add();
     });
   }
-  
+
   /**
    * 编辑
    * @param record
@@ -43,7 +54,7 @@
       registerForm.value.edit(record);
     });
   }
-  
+
   /**
    * 确定按钮点击事件
    */

+ 73 - 75
jeecgboot-vue3/src/views/spbb/video/SppbVideo.data.ts

@@ -1,132 +1,130 @@
-import {BasicColumn} from '/@/components/Table';
-import {FormSchema} from '/@/components/Table';
-import { rules} from '/@/utils/helper/validator';
-import { render } from '/@/utils/common/renderUtils';
-import { getWeekMonthQuarterYear } from '/@/utils';
+import { BasicColumn } from '/@/components/Table';
 //列表数据
 export const columns: BasicColumn[] = [
   {
     title: '视频标题',
-    align: "center",
-    dataIndex: 'title'
+    align: 'center',
+    dataIndex: 'title',
   },
   {
     title: '视频封面',
-    align: "center",
-    dataIndex: 'cover'
-  },
-  {
-    title: '视频文件',
-    align: "center",
-    dataIndex: 'videoFile'
+    align: 'center',
+    dataIndex: 'cover',
+    width: 100,
+    slots: { customRender: 'cover' },
   },
+  // {
+  //   title: '视频文件',
+  //   align: "center",
+  //   dataIndex: 'videoFile'
+  // },
   {
     title: '视频格式',
-    align: "center",
-    dataIndex: 'format'
+    align: 'center',
+    dataIndex: 'format',
   },
   {
     title: '视频文件大小',
-    align: "center",
-    dataIndex: 'fileSize'
+    align: 'center',
+    dataIndex: 'fileSize',
   },
   {
     title: '视频时长',
-    align: "center",
-    dataIndex: 'duration'
-  },
-  {
-    title: '关联业务ID',
-    align: "center",
-    dataIndex: 'dataId'
-  },
-  {
-    title: '关联业务名称',
-    align: "center",
-    dataIndex: 'dataName'
-  },
+    align: 'center',
+    dataIndex: 'duration',
+  },
+  // {
+  //   title: '关联业务ID',
+  //   align: "center",
+  //   dataIndex: 'dataId'
+  // },
+  // {
+  //   title: '关联业务名称',
+  //   align: "center",
+  //   dataIndex: 'dataName'
+  // },
   {
     title: '操作步骤摘要',
-    align: "center",
-    dataIndex: 'stepSummary'
+    align: 'center',
+    dataIndex: 'stepSummary',
   },
   {
     title: '视频内容描述',
-    align: "center",
-    dataIndex: 'contentDesc'
+    align: 'center',
+    dataIndex: 'contentDesc',
   },
   {
     title: '录制时间',
-    align: "center",
-    dataIndex: 'recordingTime'
+    align: 'center',
+    dataIndex: 'recordingTime',
   },
   {
     title: '视频内容更新时间',
-    align: "center",
-    dataIndex: 'videoUpdateTime'
+    align: 'center',
+    dataIndex: 'videoUpdateTime',
   },
   {
     title: '视频状态',
-    align: "center",
-    dataIndex: 'state'
+    align: 'center',
+    dataIndex: 'state',
   },
   {
     title: '业务分类',
-    align: "center",
-    dataIndex: 'businessCategory'
+    align: 'center',
+    dataIndex: 'businessCategory',
   },
   {
     title: '关键词',
-    align: "center",
-    dataIndex: 'keyword'
+    align: 'center',
+    dataIndex: 'keyword',
   },
   {
     title: '适用人群',
-    align: "center",
-    dataIndex: 'targetAudience'
+    align: 'center',
+    dataIndex: 'targetAudience',
   },
   {
     title: '播放次数',
-    align: "center",
-    dataIndex: 'playCount'
+    align: 'center',
+    dataIndex: 'playCount',
   },
   {
     title: '收藏次数',
-    align: "center",
-    dataIndex: 'favoriteCount'
+    align: 'center',
+    dataIndex: 'favoriteCount',
   },
   {
     title: '播放完成率',
-    align: "center",
-    dataIndex: 'playCompletionRate'
+    align: 'center',
+    dataIndex: 'playCompletionRate',
   },
   {
     title: '备注说明',
-    align: "center",
-    dataIndex: 'remark'
+    align: 'center',
+    dataIndex: 'remark',
   },
 ];
 
 // 高级查询数据
 export const superQuerySchema = {
-  title: {title: '视频标题',order: 0,view: 'text', type: 'string',},
-  cover: {title: '视频封面',order: 1,view: 'text', type: 'string',},
-  videoFile: {title: '视频文件',order: 2,view: 'text', type: 'string',},
-  format: {title: '视频格式',order: 3,view: 'text', type: 'string',},
-  fileSize: {title: '视频文件大小',order: 4,view: 'text', type: 'string',},
-  duration: {title: '视频时长',order: 5,view: 'text', type: 'string',},
-  dataId: {title: '关联业务ID',order: 6,view: 'text', type: 'string',},
-  dataName: {title: '关联业务名称',order: 7,view: 'text', type: 'string',},
-  stepSummary: {title: '操作步骤摘要',order: 8,view: 'text', type: 'string',},
-  contentDesc: {title: '视频内容描述',order: 9,view: 'text', type: 'string',},
-  recordingTime: {title: '录制时间',order: 10,view: 'datetime', type: 'string',},
-  videoUpdateTime: {title: '视频内容更新时间',order: 11,view: 'datetime', type: 'string',},
-  state: {title: '视频状态',order: 12,view: 'number', type: 'number',},
-  businessCategory: {title: '业务分类',order: 13,view: 'number', type: 'number',},
-  keyword: {title: '关键词',order: 14,view: 'text', type: 'string',},
-  targetAudience: {title: '适用人群',order: 15,view: 'text', type: 'string',},
-  playCount: {title: '播放次数',order: 16,view: 'number', type: 'number',},
-  favoriteCount: {title: '收藏次数',order: 17,view: 'number', type: 'number',},
-  playCompletionRate: {title: '播放完成率',order: 18,view: 'text', type: 'string',},
-  remark: {title: '备注说明',order: 19,view: 'text', type: 'string',},
+  title: { title: '视频标题', order: 0, view: 'text', type: 'string' },
+  // cover: {title: '视频封面',order: 1,view: 'text', type: 'string',},
+  // videoFile: {title: '视频文件',order: 2,view: 'text', type: 'string',},
+  format: { title: '视频格式', order: 3, view: 'text', type: 'string' },
+  fileSize: { title: '视频文件大小', order: 4, view: 'text', type: 'string' },
+  duration: { title: '视频时长', order: 5, view: 'text', type: 'string' },
+  // dataId: {title: '关联业务ID',order: 6,view: 'text', type: 'string',},
+  // dataName: {title: '关联业务名称',order: 7,view: 'text', type: 'string',},
+  stepSummary: { title: '操作步骤摘要', order: 8, view: 'text', type: 'string' },
+  contentDesc: { title: '视频内容描述', order: 9, view: 'text', type: 'string' },
+  recordingTime: { title: '录制时间', order: 10, view: 'datetime', type: 'string' },
+  videoUpdateTime: { title: '视频内容更新时间', order: 11, view: 'datetime', type: 'string' },
+  state: { title: '视频状态', order: 12, view: 'number', type: 'number' },
+  businessCategory: { title: '业务分类', order: 13, view: 'number', type: 'number' },
+  keyword: { title: '关键词', order: 14, view: 'text', type: 'string' },
+  targetAudience: { title: '适用人群', order: 15, view: 'text', type: 'string' },
+  playCount: { title: '播放次数', order: 16, view: 'number', type: 'number' },
+  favoriteCount: { title: '收藏次数', order: 17, view: 'number', type: 'number' },
+  playCompletionRate: { title: '播放完成率', order: 18, view: 'text', type: 'string' },
+  remark: { title: '备注说明', order: 19, view: 'text', type: 'string' },
 };

+ 56 - 59
jeecgboot-vue3/src/views/spbb/video/SppbVideoList.vue

@@ -1,20 +1,18 @@
-
-<template>
+<template>
   <div class="p-2">
     <!--查询区域-->
     <div class="jeecg-basic-table-form-container">
       <a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
-        <a-row :gutter="24">
-        </a-row>
+        <a-row :gutter="24"> </a-row>
       </a-form>
     </div>
     <!--引用表格-->
     <BasicTable @register="registerTable" :rowSelection="rowSelection">
       <!--插槽:table标题-->
       <template #tableTitle>
-        <a-button type="primary" v-auth="'spbb:sppb_video:add'"  @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
-        <a-button  type="primary" v-auth="'spbb:sppb_video:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
-        <j-upload-button  type="primary" v-auth="'spbb:sppb_video:importExcel'"  preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
+        <a-button v-auth="'spbb:sppb_video:add'" preIcon="ant-design:plus-outlined" type="primary" @click="handleAdd"> 新增</a-button>
+        <a-button v-auth="'spbb:sppb_video:exportXls'" preIcon="ant-design:export-outlined" type="primary" @click="onExportXls"> 导出</a-button>
+        <!--        <j-upload-button  type="primary" v-auth="'spbb:sppb_video:importExcel'"  preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>-->
         <a-dropdown v-if="selectedRowKeys.length > 0">
           <template #overlay>
             <a-menu>
@@ -24,7 +22,8 @@
               </a-menu-item>
             </a-menu>
           </template>
-          <a-button v-auth="'spbb:sppb_video:deleteBatch'">批量操作
+          <a-button v-auth="'spbb:sppb_video:deleteBatch'"
+            >批量操作
             <Icon icon="mdi:chevron-down"></Icon>
           </a-button>
         </a-dropdown>
@@ -33,9 +32,12 @@
       </template>
       <!--操作栏-->
       <template #action="{ record }">
-        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
       </template>
-      <template v-slot:bodyCell="{ column, record, index, text }">
+      <template v-slot:bodyCell="{ column, record, index, text }"> </template>
+      <template #cover="{ text }">
+        <span v-if="!text">未上传封面</span>
+        <a-image v-else :src="getFileAccessHttpUrl(text)" alt="封面" style="width: 60px; height: 60px; object-fit: cover; border-radius: 4px" />
       </template>
     </BasicTable>
     <!-- 表单区域 -->
@@ -44,20 +46,18 @@
 </template>
 
 <script lang="ts" name="spbb-sppbVideo" setup>
-  import { ref, reactive } from 'vue';
-  import { BasicTable, useTable, TableAction } from '/@/components/Table';
-  import { useListPage } from '/@/hooks/system/useListPage';
-  import { columns, superQuerySchema } from './SppbVideo.data';
-  import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './SppbVideo.api';
-  import { downloadFile } from '/@/utils/common/renderUtils';
-  import SppbVideoModal from './components/SppbVideoModal.vue'
-  import { useUserStore } from '/@/store/modules/user';
-  import { useMessage } from '/@/hooks/web/useMessage';
-   import {useModal} from '/@/components/Modal';
-  import { getDateByPicker } from '/@/utils';
-
-  const fieldPickers = reactive({
-  });
+import { reactive, ref } from 'vue';
+import { BasicTable, TableAction } from '/@/components/Table';
+import { useListPage } from '/@/hooks/system/useListPage';
+import { columns, superQuerySchema } from './SppbVideo.data';
+import { batchDelete, deleteOne, getExportUrl, getImportUrl, list } from './SppbVideo.api';
+import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
+import SppbVideoModal from './components/SppbVideoModal.vue';
+import { useUserStore } from '/@/store/modules/user';
+import { useMessage } from '/@/hooks/web/useMessage';
+import { getDateByPicker } from '/@/utils';
+
+const fieldPickers = reactive({});
 
   const formRef = ref();
   const queryParam = reactive<any>({});
@@ -71,7 +71,7 @@
       title: '视频帮办-视频管理',
       api: list,
       columns,
-      canResize:true,
+      canResize: true,
       useSearchForm: false,
       actionColumn: {
         width: 120,
@@ -87,21 +87,22 @@
       },
     },
     exportConfig: {
-      name: "视频帮办-视频管理",
+      name: '视频帮办-视频管理',
       url: getExportUrl,
       params: queryParam,
     },
-	  importConfig: {
-	    url: getImportUrl,
-	    success: handleSuccess
-	  },
+    importConfig: {
+      url: getImportUrl,
+      success: handleSuccess,
+    },
   });
-  const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
+  const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
+    tableContext;
   const labelCol = reactive({
-    xs:24,
-    sm:4,
-    xl:6,
-    xxl:4
+    xs: 24,
+    sm: 4,
+    xl: 6,
+    xxl: 4,
   });
   const wrapperCol = reactive({
     xs: 24,
@@ -128,7 +129,7 @@
     registerModal.value.disableSubmit = false;
     registerModal.value.add();
   }
-  
+
   /**
    * 编辑事件
    */
@@ -136,7 +137,7 @@
     registerModal.value.disableSubmit = false;
     registerModal.value.edit(record);
   }
-   
+
   /**
    * 详情
    */
@@ -144,28 +145,28 @@
     registerModal.value.disableSubmit = true;
     registerModal.value.edit(record);
   }
-   
+
   /**
    * 删除事件
    */
   async function handleDelete(record) {
     await deleteOne({ id: record.id }, handleSuccess);
   }
-   
+
   /**
    * 批量删除事件
    */
   async function batchHandleDelete() {
     await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
   }
-   
+
   /**
    * 成功回调
    */
   function handleSuccess() {
     (selectedRowKeys.value = []) && reload();
   }
-   
+
   /**
    * 操作栏
    */
@@ -174,11 +175,11 @@
       {
         label: '编辑',
         onClick: handleEdit.bind(null, record),
-        auth: 'spbb:sppb_video:edit'
+        auth: 'spbb:sppb_video:edit',
       },
     ];
   }
-   
+
   /**
    * 下拉操作栏
    */
@@ -187,16 +188,17 @@
       {
         label: '详情',
         onClick: handleDetail.bind(null, record),
-      }, {
+      },
+      {
         label: '删除',
         popConfirm: {
           title: '是否确认删除',
           confirm: handleDelete.bind(null, record),
           placement: 'topLeft',
         },
-        auth: 'spbb:sppb_video:delete'
-      }
-    ]
+        auth: 'spbb:sppb_video:delete',
+      },
+    ];
   }
 
   /**
@@ -205,7 +207,7 @@
   function searchQuery() {
     reload();
   }
-  
+
   /**
    * 重置
    */
@@ -215,12 +217,6 @@
     //刷新数据
     reload();
   }
-  
-
-
-
-
-
 </script>
 
 <style lang="less" scoped>
@@ -231,19 +227,20 @@
       margin-bottom: 24px;
       white-space: nowrap;
     }
-    .query-group-cust{
+    .query-group-cust {
       min-width: 100px !important;
     }
-    .query-group-split-cust{
+    .query-group-split-cust {
       width: 30px;
       display: inline-block;
-      text-align: center
+      text-align: center;
     }
-    .ant-form-item:not(.ant-form-item-with-help){
+    .ant-form-item:not(.ant-form-item-with-help) {
       margin-bottom: 16px;
       height: 32px;
     }
-    :deep(.ant-picker),:deep(.ant-input-number){
+    :deep(.ant-picker),
+    :deep(.ant-input-number) {
       width: 100%;
     }
   }

+ 60 - 42
jeecgboot-vue3/src/views/spbb/video/components/SppbVideoForm.vue

@@ -5,101 +5,106 @@
         <a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="SppbVideoForm">
           <a-row>
 						<a-col :span="24">
-							<a-form-item label="视频标题" v-bind="validateInfos.title" id="SppbVideoForm-title" name="title">
+							<a-form-item id="SppbVideoForm-title" :labelCol="{ xs: { span: 24 }, sm: { span: 3 } }" :wrapperCol="{ xs: { span: 24 }, sm: { span: 20 } }" label="视频标题" name="title" v-bind="validateInfos.title">
 								<a-input v-model:value="formData.title" placeholder="请输入视频标题"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="视频封面" v-bind="validateInfos.cover" id="SppbVideoForm-cover" name="cover">
 								<JImageUpload v-model:value="formData.cover" :fileMax="1" bizPath="cover" :disabled="disabled" />
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col v-if="disabled && videoUrl" :span="24">
+							<a-form-item id="SppbVideoForm-videoFile-detail" :labelCol="{ xs: { span: 24 }, sm: { span: 3 } }" :wrapperCol="{ xs: { span: 24 }, sm: { span: 20 } }" label="视频文件" name="videoFile">
+								<video :src="videoUrl" controls preload="metadata" style="width: 70%; max-height: 500px; border-radius: 8px; background: #000;" />
+							</a-form-item>
+						</a-col>
+						<a-col v-else :span="12">
 							<a-form-item label="视频文件" v-bind="validateInfos.videoFile" id="SppbVideoForm-videoFile" name="videoFile">
 								<JUpload v-model:value="formData.videoFile" :maxCount="1" :multiple="false" bizPath="video" :disabled="disabled" :beforeUpload="beforeVideoUpload" accept="video/*" />
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="视频格式" v-bind="validateInfos.format" id="SppbVideoForm-format" name="format">
 								<a-input v-model:value="formData.format" placeholder="请输入视频格式"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="视频文件大小" v-bind="validateInfos.fileSize" id="SppbVideoForm-fileSize" name="fileSize">
 								<a-input v-model:value="formData.fileSize" placeholder="请输入视频文件大小"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="视频时长" v-bind="validateInfos.duration" id="SppbVideoForm-duration" name="duration">
 								<a-input v-model:value="formData.duration" placeholder="请输入视频时长"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="关联业务ID" v-bind="validateInfos.dataId" id="SppbVideoForm-dataId" name="dataId">
 								<a-input v-model:value="formData.dataId" placeholder="请输入关联业务ID"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="关联业务名称" v-bind="validateInfos.dataName" id="SppbVideoForm-dataName" name="dataName">
 								<a-input v-model:value="formData.dataName" placeholder="请输入关联业务名称"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="操作步骤摘要" v-bind="validateInfos.stepSummary" id="SppbVideoForm-stepSummary" name="stepSummary">
 								<a-input v-model:value="formData.stepSummary" placeholder="请输入操作步骤摘要"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="视频内容描述" v-bind="validateInfos.contentDesc" id="SppbVideoForm-contentDesc" name="contentDesc">
 								<a-input v-model:value="formData.contentDesc" placeholder="请输入视频内容描述"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="录制时间" v-bind="validateInfos.recordingTime" id="SppbVideoForm-recordingTime" name="recordingTime">
 								<a-date-picker placeholder="请选择录制时间"  v-model:value="formData.recordingTime" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%"  allow-clear />
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="视频内容更新时间" v-bind="validateInfos.videoUpdateTime" id="SppbVideoForm-videoUpdateTime" name="videoUpdateTime">
 								<a-date-picker placeholder="请选择视频内容更新时间"  v-model:value="formData.videoUpdateTime" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%"  allow-clear />
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="视频状态" v-bind="validateInfos.state" id="SppbVideoForm-state" name="state">
 								<a-input-number v-model:value="formData.state" placeholder="请输入视频状态" style="width: 100%" />
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="业务分类" v-bind="validateInfos.businessCategory" id="SppbVideoForm-businessCategory" name="businessCategory">
 								<a-input-number v-model:value="formData.businessCategory" placeholder="请输入业务分类" style="width: 100%" />
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="关键词" v-bind="validateInfos.keyword" id="SppbVideoForm-keyword" name="keyword">
 								<a-input v-model:value="formData.keyword" placeholder="请输入关键词"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
+						<a-col :span="12">
 							<a-form-item label="适用人群" v-bind="validateInfos.targetAudience" id="SppbVideoForm-targetAudience" name="targetAudience">
 								<a-input v-model:value="formData.targetAudience" placeholder="请输入适用人群"  allow-clear ></a-input>
 							</a-form-item>
 						</a-col>
-						<a-col :span="24">
-							<a-form-item label="播放次数" v-bind="validateInfos.playCount" id="SppbVideoForm-playCount" name="playCount">
-								<a-input-number v-model:value="formData.playCount" placeholder="请输入播放次数" style="width: 100%" />
-							</a-form-item>
-						</a-col>
-						<a-col :span="24">
-							<a-form-item label="收藏次数" v-bind="validateInfos.favoriteCount" id="SppbVideoForm-favoriteCount" name="favoriteCount">
-								<a-input-number v-model:value="formData.favoriteCount" placeholder="请输入收藏次数" style="width: 100%" />
-							</a-form-item>
-						</a-col>
-						<a-col :span="24">
-							<a-form-item label="播放完成率" v-bind="validateInfos.playCompletionRate" id="SppbVideoForm-playCompletionRate" name="playCompletionRate">
-								<a-input v-model:value="formData.playCompletionRate" placeholder="请输入播放完成率"  allow-clear ></a-input>
-							</a-form-item>
-						</a-col>
-						<a-col :span="24">
+<!--						<a-col :span="12">-->
+<!--							<a-form-item label="播放次数" v-bind="validateInfos.playCount" id="SppbVideoForm-playCount" name="playCount">-->
+<!--								<a-input-number v-model:value="formData.playCount" placeholder="请输入播放次数" style="width: 100%" />-->
+<!--							</a-form-item>-->
+<!--						</a-col>-->
+<!--						<a-col :span="12">-->
+<!--							<a-form-item label="收藏次数" v-bind="validateInfos.favoriteCount" id="SppbVideoForm-favoriteCount" name="favoriteCount">-->
+<!--								<a-input-number v-model:value="formData.favoriteCount" placeholder="请输入收藏次数" style="width: 100%" />-->
+<!--							</a-form-item>-->
+<!--						</a-col>-->
+<!--						<a-col :span="12">-->
+<!--							<a-form-item label="播放完成率" v-bind="validateInfos.playCompletionRate" id="SppbVideoForm-playCompletionRate" name="playCompletionRate">-->
+<!--								<a-input v-model:value="formData.playCompletionRate" placeholder="请输入播放完成率"  allow-clear ></a-input>-->
+<!--							</a-form-item>-->
+<!--						</a-col>-->
+						<a-col :span="12">
 							<a-form-item label="备注说明" v-bind="validateInfos.remark" id="SppbVideoForm-remark" name="remark">
 								<a-input v-model:value="formData.remark" placeholder="请输入备注说明"  allow-clear ></a-input>
 							</a-form-item>
@@ -112,15 +117,17 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive, defineExpose, nextTick, defineProps, computed } from 'vue';
-  import { useMessage } from '/@/hooks/web/useMessage';
-  import { getDateByPicker, getValueType } from '/@/utils';
-  import { saveOrUpdate } from '../SppbVideo.api';
-  import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
-  import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
-  import { Form } from 'ant-design-vue';
-  import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
-  const props = defineProps({
+import { computed, defineExpose, defineProps, nextTick, reactive, ref } from 'vue';
+import { useMessage } from '/@/hooks/web/useMessage';
+import { getDateByPicker, getValueType } from '/@/utils';
+import { saveOrUpdate } from '../SppbVideo.api';
+import JImageUpload from '/@/components/Form/src/jeecg/components/JImageUpload.vue';
+import JUpload from '/@/components/Form/src/jeecg/components/JUpload/JUpload.vue';
+import { Form } from 'ant-design-vue';
+import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue';
+import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
+
+const props = defineProps({
     formDisabled: { type: Boolean, default: false },
     formData: { type: Object, default: () => ({})},
     formBpm: { type: Boolean, default: true }
@@ -152,7 +159,7 @@
     remark: '',   
   });
   const { createMessage } = useMessage();
-  const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
+  const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 6 } });
   const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
   const confirmLoading = ref<boolean>(false);
 
@@ -185,6 +192,13 @@
     return props.formDisabled;
   });
 
+  const videoUrl = computed(() => {
+    if (formData.videoFile) {
+      return getFileAccessHttpUrl(formData.videoFile);
+    }
+    return '';
+  });
+
   
   /**
    * 新增
@@ -272,4 +286,8 @@
   .antd-modal-form {
     padding: 14px 20px;
   }
+  :deep(.jeecg-form-container-disabled fieldset[disabled] video) {
+    -ms-pointer-events: auto !important;
+    pointer-events: auto !important;
+  }
 </style>

+ 23 - 12
jeecgboot-vue3/src/views/spbb/video/components/SppbVideoModal.vue

@@ -1,19 +1,30 @@
 <template>
-  <j-modal :title="title" maxHeight="500px" :width="800" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
+  <j-modal
+    :fullscreen="true"
+    :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }"
+    :title="title"
+    :visible="visible"
+    :width="800"
+    cancelText="关闭"
+    maxHeight="500px"
+    @cancel="handleCancel"
+    @ok="handleOk"
+  >
     <SppbVideoForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></SppbVideoForm>
-   <template #footer>
-     <a-button @click="handleCancel">取消</a-button>
-     <a-button :class="{ 'jee-hidden': disableSubmit }" type="primary" @click="handleOk">确认</a-button>
-   </template>
+    <template #footer>
+      <a-button @click="handleCancel">取消</a-button>
+      <a-button :class="{ 'jee-hidden': disableSubmit }" type="primary" @click="handleOk">确认</a-button>
+    </template>
   </j-modal>
 </template>
 
 <script lang="ts" setup>
-  import { ref, nextTick, defineExpose } from 'vue';
-  import SppbVideoForm from './SppbVideoForm.vue'
-  import JModal from '/@/components/Modal/src/JModal/JModal.vue';
-  import { useMessage } from '/@/hooks/web/useMessage';
-  const { createMessage } = useMessage();
+import {defineExpose, nextTick, ref} from 'vue';
+import SppbVideoForm from './SppbVideoForm.vue';
+import JModal from '/@/components/Modal/src/JModal/JModal.vue';
+import {useMessage} from '/@/hooks/web/useMessage';
+
+const { createMessage } = useMessage();
   const title = ref<string>('');
   const width = ref<number>(800);
   const visible = ref<boolean>(false);
@@ -31,7 +42,7 @@
       registerForm.value.add();
     });
   }
-  
+
   /**
    * 编辑
    * @param record
@@ -43,7 +54,7 @@
       registerForm.value.edit(record);
     });
   }
-  
+
   /**
    * 确定按钮点击事件
    */