Explorar el Código

素材播放优化修改

zmj hace 1 semana
padre
commit
94447c6ecb
Se han modificado 1 ficheros con 113 adiciones y 182 borrados
  1. 113 182
      src/views/base/plan/index.vue

+ 113 - 182
src/views/base/plan/index.vue

@@ -2,13 +2,8 @@
   <div class="app-container">
     <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="90px">
       <el-form-item label="方案名称" prop="planName">
-        <el-input
-          v-model="queryParams.planName"
-          placeholder="请输入方案名称"
-          clearable
-          style="width: 200px"
-          @keyup.enter="handleQuery"
-        />
+        <el-input v-model="queryParams.planName" placeholder="请输入方案名称" clearable style="width: 200px"
+          @keyup.enter="handleQuery" />
       </el-form-item>
       <el-form-item label="循环方式" prop="loopMode">
         <el-select v-model="queryParams.loopMode" placeholder="请选择循环方式" clearable style="width: 160px">
@@ -30,54 +25,25 @@
 
     <el-row :gutter="10" class="mb8">
       <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="Plus"
-          @click="handleAdd"
-          v-hasPermi="['base:plan:add']"
-        >新增</el-button>
+        <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['base:plan:add']">新增</el-button>
       </el-col>
       <el-col :span="1.5">
-        <el-button
-          type="success"
-          plain
-          icon="Edit"
-          :disabled="single"
-          @click="handleUpdate"
-          v-hasPermi="['base:plan:edit']"
-        >修改</el-button>
+        <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
+          v-hasPermi="['base:plan:edit']">修改</el-button>
       </el-col>
       <el-col :span="1.5">
-        <el-button
-          type="danger"
-          plain
-          icon="Delete"
-          :disabled="multiple"
-          @click="handleDelete"
-          v-hasPermi="['base:plan:remove']"
-        >删除</el-button>
+        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
+          v-hasPermi="['base:plan:remove']">删除</el-button>
       </el-col>
       <el-col :span="1.5">
-        <el-button
-          type="warning"
-          plain
-          icon="Download"
-          @click="handleExport"
-          v-hasPermi="['base:plan:export']"
-        >导出</el-button>
+        <el-button type="warning" plain icon="Download" @click="handleExport"
+          v-hasPermi="['base:plan:export']">导出</el-button>
       </el-col>
       <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-alert
-      v-if="currentPlayingCount > 1"
-      title="检测到存在多条当前播放方案,请联系后端检查播放状态唯一性规则。"
-      type="warning"
-      show-icon
-      :closable="false"
-      class="play-status-alert"
-    />
+    <el-alert v-if="currentPlayingCount > 1" title="检测到存在多条当前播放方案,请联系后端检查播放状态唯一性规则。" type="warning" show-icon
+      :closable="false" class="play-status-alert" />
 
     <el-table v-loading="loading" :data="planList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
@@ -104,35 +70,20 @@
       <el-table-column label="操作" align="center" width="240" fixed="right" class-name="small-padding fixed-width">
         <template #default="scope">
           <el-button link type="primary" icon="View" @click="handlePreview(scope.row)">预览</el-button>
-          <el-button
-            v-if="String(scope.row.status) !== '1'"
-            link
-            type="success"
-            icon="CircleCheck"
-            @click="handleStatusChange(scope.row, '1')"
-            v-hasPermi="['base:plan:edit']"
-          >启用播放</el-button>
-          <el-button
-            v-if="String(scope.row.status) === '1'"
-            link
-            type="warning"
-            icon="CircleClose"
-            @click="handleStatusChange(scope.row, '0')"
-            v-hasPermi="['base:plan:edit']"
-          >设为备用</el-button>
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['base:plan:edit']">编辑</el-button>
-          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['base:plan:remove']">删除</el-button>
+          <el-button v-if="String(scope.row.status) !== '1'" link type="success" icon="CircleCheck"
+            @click="handleStatusChange(scope.row, '1')" v-hasPermi="['base:plan:edit']">启用播放</el-button>
+          <el-button v-if="String(scope.row.status) === '1'" link type="warning" icon="CircleClose"
+            @click="handleStatusChange(scope.row, '0')" v-hasPermi="['base:plan:edit']">设为备用</el-button>
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)"
+            v-hasPermi="['base:plan:edit']">编辑</el-button>
+          <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)"
+            v-hasPermi="['base:plan:remove']">删除</el-button>
         </template>
       </el-table-column>
     </el-table>
 
-    <pagination
-      v-show="total>0"
-      :total="total"
-      v-model:page="queryParams.pageNum"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
+      v-model:limit="queryParams.pageSize" @pagination="getList" />
 
     <!-- 添加或修改播放方案对话框 -->
     <el-dialog :title="title" v-model="open" width="960px" append-to-body>
@@ -140,12 +91,7 @@
         <el-row>
           <el-col :span="24">
             <el-form-item label="方案名称" prop="planName">
-              <el-input
-                v-model="form.planName"
-                maxlength="100"
-                show-word-limit
-                placeholder="请输入方案名称"
-              />
+              <el-input v-model="form.planName" maxlength="100" show-word-limit placeholder="请输入方案名称" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -167,14 +113,8 @@
           </el-col>
           <el-col :span="24">
             <el-form-item label="备注" prop="remark">
-              <el-input
-                v-model="form.remark"
-                type="textarea"
-                :rows="3"
-                maxlength="500"
-                show-word-limit
-                placeholder="请输入备注"
-              />
+              <el-input v-model="form.remark" type="textarea" :rows="3" maxlength="500" show-word-limit
+                placeholder="请输入备注" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -187,25 +127,19 @@
             <el-button type="danger" icon="Delete" @click="handleDeleteRobotOpsPlayPlanItem">移除素材</el-button>
           </el-col>
         </el-row>
-        <el-table
-          :data="robotOpsPlayPlanItemList"
-          @selection-change="handleRobotOpsPlayPlanItemSelectionChange"
-          ref="robotOpsPlayPlanItem"
-          empty-text="请点击「选择素材」添加播放内容"
-        >
+        <el-table :data="robotOpsPlayPlanItemList" @selection-change="handleRobotOpsPlayPlanItemSelectionChange"
+          ref="robotOpsPlayPlanItem" empty-text="请点击「选择素材」添加播放内容">
           <el-table-column type="selection" width="50" align="center" />
           <el-table-column label="素材" min-width="260">
             <template #default="scope">
               <div class="asset-info">
                 <div class="asset-thumb">
-                  <image-preview
-                    v-if="getAssetThumb(scope.row)"
-                    :src="getAssetThumb(scope.row)"
-                    :width="54"
-                    :height="54"
-                  />
+                  <image-preview v-if="getAssetThumb(scope.row)" :src="getAssetThumb(scope.row)" :width="54"
+                    :height="54" />
                   <div v-else class="video-thumb">
-                    <el-icon><VideoPlay /></el-icon>
+                    <el-icon>
+                      <VideoPlay />
+                    </el-icon>
                   </div>
                 </div>
                 <div class="asset-meta">
@@ -222,21 +156,16 @@
           </el-table-column>
           <el-table-column label="顺序" prop="playOrder" width="140" align="center">
             <template #default="scope">
-              <el-button link type="primary" :disabled="scope.$index === 0" @click="moveItem(scope.$index, -1)">上移</el-button>
-              <el-button link type="primary" :disabled="scope.$index === robotOpsPlayPlanItemList.length - 1" @click="moveItem(scope.$index, 1)">下移</el-button>
+              <el-button link type="primary" :disabled="scope.$index === 0"
+                @click="moveItem(scope.$index, -1)">上移</el-button>
+              <el-button link type="primary" :disabled="scope.$index === robotOpsPlayPlanItemList.length - 1"
+                @click="moveItem(scope.$index, 1)">下移</el-button>
             </template>
           </el-table-column>
           <el-table-column label="停留时长" prop="staySeconds" width="160" align="center">
             <template #default="scope">
-              <el-input-number
-                v-if="scope.row.assetType === 'image'"
-                v-model="scope.row.staySeconds"
-                :min="1"
-                :max="3600"
-                :step="1"
-                controls-position="right"
-                style="width: 120px"
-              />
+              <el-input-number v-if="scope.row.assetType === 'image'" v-model="scope.row.staySeconds" :min="1"
+                :max="3600" :step="1" controls-position="right" style="width: 120px" />
               <span v-else class="text-muted">视频播完切换</span>
             </template>
           </el-table-column>
@@ -263,12 +192,8 @@
     <el-dialog title="选择素材" v-model="assetSelectOpen" width="900px" append-to-body @closed="selectedAssets = []">
       <el-form :model="assetQueryParams" :inline="true" label-width="80px">
         <el-form-item label="素材名称">
-          <el-input
-            v-model="assetQueryParams.assetName"
-            placeholder="请输入素材名称"
-            clearable
-            @keyup.enter="handleAssetQuery"
-          />
+          <el-input v-model="assetQueryParams.assetName" placeholder="请输入素材名称" clearable
+            @keyup.enter="handleAssetQuery" />
         </el-form-item>
         <el-form-item label="素材类型">
           <el-select v-model="assetQueryParams.assetType" placeholder="请选择素材类型" clearable style="width: 140px">
@@ -281,25 +206,16 @@
           <el-button icon="Refresh" @click="resetAssetQuery">重置</el-button>
         </el-form-item>
       </el-form>
-      <el-alert
-        title="仅显示启用状态的素材;已停用素材不能被新播放方案选择。"
-        type="info"
-        show-icon
-        :closable="false"
-        class="asset-select-tip"
-      />
+      <el-alert title="仅显示启用状态的素材;已停用素材不能被新播放方案选择。" type="info" show-icon :closable="false" class="asset-select-tip" />
       <el-table v-loading="assetLoading" :data="assetList" @selection-change="handleAssetSelectionChange">
         <el-table-column type="selection" width="50" align="center" />
         <el-table-column label="缩略图" width="90" align="center">
           <template #default="scope">
-            <image-preview
-              v-if="getAssetThumb(scope.row)"
-              :src="getAssetThumb(scope.row)"
-              :width="48"
-              :height="48"
-            />
+            <image-preview v-if="getAssetThumb(scope.row)" :src="getAssetThumb(scope.row)" :width="48" :height="48" />
             <div v-else class="video-thumb small">
-              <el-icon><VideoPlay /></el-icon>
+              <el-icon>
+                <VideoPlay />
+              </el-icon>
             </div>
           </template>
         </el-table-column>
@@ -318,13 +234,8 @@
           <template #default="scope">{{ formatDuration(scope.row.durationSeconds) }}</template>
         </el-table-column>
       </el-table>
-      <pagination
-        v-show="assetTotal > 0"
-        :total="assetTotal"
-        v-model:page="assetQueryParams.pageNum"
-        v-model:limit="assetQueryParams.pageSize"
-        @pagination="getAssetList"
-      />
+      <pagination v-show="assetTotal > 0" :total="assetTotal" v-model:page="assetQueryParams.pageNum"
+        v-model:limit="assetQueryParams.pageSize" @pagination="getAssetList" />
       <template #footer>
         <div class="dialog-footer">
           <el-button type="primary" @click="confirmSelectAsset">确 定</el-button>
@@ -334,13 +245,8 @@
     </el-dialog>
 
     <!-- 播放方案预览弹窗 -->
-    <el-dialog
-      :title="previewPlan?.planName ? '播放方案预览 - ' + previewPlan.planName : '播放方案预览'"
-      v-model="previewOpen"
-      width="960px"
-      append-to-body
-      @closed="resetPreview"
-    >
+    <el-dialog :title="previewPlan?.planName ? '播放方案预览 - ' + previewPlan.planName : '播放方案预览'" v-model="previewOpen"
+      width="960px" append-to-body @closed="resetPreview">
       <div v-if="previewPlan" class="plan-preview">
         <div class="preview-header">
           <div>
@@ -359,23 +265,15 @@
           <div class="preview-main">
             <div class="preview-stage">
               <div v-if="currentPreviewItem && currentPreviewItem.fileUrl">
-                <img
-                  v-if="currentPreviewItem.assetType === 'image'"
-                  :src="currentPreviewItem.fileUrl"
-                  class="preview-media preview-image"
-                  @error="handlePreviewMediaError"
-                />
-                <video
-                  v-else-if="currentPreviewItem.assetType === 'video'"
-                  ref="previewVideoRef"
-                  :src="currentPreviewItem.fileUrl"
-                  class="preview-media preview-video"
-                  controls
-                  @error="handlePreviewMediaError"
-                />
+                <img v-if="currentPreviewItem.assetType === 'image'" :src="currentPreviewItem.fileUrl"
+                  class="preview-media preview-image" @error="handlePreviewMediaError" />
+                <video v-else-if="currentPreviewItem.assetType === 'video'" ref="previewVideoRef"
+                  :src="currentPreviewItem.fileUrl" class="preview-media preview-video" controls
+                  @error="handlePreviewMediaError" />
                 <div v-else class="preview-empty">暂不支持该素材类型预览</div>
               </div>
-              <div v-else-if="currentPreviewItem && !currentPreviewItem.fileUrl" class="preview-empty">素材文件地址为空,无法预览</div>
+              <div v-else-if="currentPreviewItem && !currentPreviewItem.fileUrl" class="preview-empty">素材文件地址为空,无法预览
+              </div>
               <div v-else class="preview-empty">暂无可预览素材</div>
             </div>
             <div v-if="currentPreviewItem" class="preview-current-info">
@@ -383,32 +281,26 @@
               <div class="preview-current-desc">
                 <el-tag v-if="currentPreviewItem.assetType === 'image'" size="small" type="success">图片</el-tag>
                 <el-tag v-else-if="currentPreviewItem.assetType === 'video'" size="small" type="warning">视频</el-tag>
-                <span v-if="currentPreviewItem.assetType === 'image'">停留 {{ currentPreviewItem.staySeconds || '-' }} 秒</span>
+                <span v-if="currentPreviewItem.assetType === 'image'">停留 {{ currentPreviewItem.staySeconds || '-' }}
+                  秒</span>
                 <span v-else-if="currentPreviewItem.assetType === 'video'">视频播完切换</span>
                 <span v-if="currentPreviewItem.resolution">分辨率:{{ currentPreviewItem.resolution }}</span>
-                <span v-if="currentPreviewItem.durationSeconds">时长:{{ formatDuration(currentPreviewItem.durationSeconds) }}</span>
+                <span v-if="currentPreviewItem.durationSeconds">时长:{{ formatDuration(currentPreviewItem.durationSeconds)
+                }}</span>
               </div>
             </div>
           </div>
           <div class="preview-list">
             <div class="preview-list-title">播放清单</div>
-            <div
-              v-for="(item, index) in previewItems"
-              :key="item.assetId || index"
-              class="preview-item"
-              :class="{ active: index === previewIndex }"
-              @click="setPreviewIndex(index)"
-            >
+            <div v-for="(item, index) in previewItems" :key="item.assetId || index" class="preview-item"
+              :class="{ active: index === previewIndex }" @click="setPreviewIndex(index)">
               <div class="preview-item-index">{{ index + 1 }}</div>
               <div class="preview-item-thumb">
-                <image-preview
-                  v-if="getAssetThumb(item)"
-                  :src="getAssetThumb(item)"
-                  :width="44"
-                  :height="44"
-                />
+                <image-preview v-if="getAssetThumb(item)" :src="getAssetThumb(item)" :width="44" :height="44" />
                 <div v-else class="video-thumb small">
-                  <el-icon><VideoPlay /></el-icon>
+                  <el-icon>
+                    <VideoPlay />
+                  </el-icon>
                 </div>
               </div>
               <div class="preview-item-meta">
@@ -426,7 +318,8 @@
         </div>
         <div class="preview-actions">
           <el-button :disabled="previewIndex <= 0" @click="setPreviewIndex(previewIndex - 1)">上一个</el-button>
-          <el-button :disabled="previewIndex >= previewItems.length - 1" @click="setPreviewIndex(previewIndex + 1)">下一个</el-button>
+          <el-button :disabled="previewIndex >= previewItems.length - 1"
+            @click="setPreviewIndex(previewIndex + 1)">下一个</el-button>
         </div>
       </div>
     </el-dialog>
@@ -665,7 +558,7 @@ function submitForm() {
     if (String(form.value.status) === '1') {
       proxy.$modal.confirm('设为当前播放后,其他播放方案应自动变为备用方案,确认继续吗?').then(() => {
         doSubmitForm()
-      }).catch(() => {})
+      }).catch(() => { })
       return
     }
     doSubmitForm()
@@ -709,12 +602,12 @@ function doSubmitForm() {
 /** 删除按钮操作 */
 function handleDelete(row) {
   const _ids = row.id || ids.value
-  proxy.$modal.confirm('确认删除选中的播放方案吗?如删除的是当前播放方案,可能导致车端无可播放方案,请谨慎操作。').then(function() {
+  proxy.$modal.confirm('确认删除选中的播放方案吗?如删除的是当前播放方案,可能导致车端无可播放方案,请谨慎操作。').then(function () {
     return delPlan(_ids)
   }).then(() => {
     getList()
     proxy.$modal.msgSuccess("删除成功")
-  }).catch(() => {})
+  }).catch(() => { })
 }
 
 /** 播放方案素材明细删除按钮操作 */
@@ -786,8 +679,8 @@ function handlePreviewMediaError() {
 function handleStatusChange(row, targetStatus) {
   const isEnable = targetStatus === '1'
   const actionText = isEnable ? '启用播放' : '设为备用'
-  const confirmMsg = isEnable 
-    ? '确认启动播放吗?注意:系统通常只允许一个方案处于当前播放状态,其他方案可能会自动转为备用。' 
+  const confirmMsg = isEnable
+    ? '确认启动播放吗?注意:系统通常只允许一个方案处于当前播放状态,其他方案可能会自动转为备用。'
     : '确认设为备用吗?'
 
   // 修改点:添加第二个参数对象,设置 dangerouslyUseHTMLString: true
@@ -799,15 +692,15 @@ function handleStatusChange(row, targetStatus) {
   }).then(() => {
     // 1. 显示加载状态,防止重复点击
     loading.value = true
-    
+
     // 2. 获取该方案的最新完整数据
     return getPlan(row.id)
   }).then(response => {
     const data = response.data || {}
-    
+
     // 3. 构建提交 payload,仅修改 status,保留其他所有数据(包括素材列表)
     const itemList = data.robotOpsPlayPlanItemList || data.itemList || []
-    
+
     // 确保素材列表格式正确(复用 submitForm 中的处理逻辑)
     const submitItemList = itemList.map(item => ({
       id: item.id,
@@ -945,6 +838,7 @@ getList()
   align-items: center;
   gap: 10px;
 }
+
 .asset-thumb {
   width: 54px;
   height: 54px;
@@ -952,6 +846,7 @@ getList()
   align-items: center;
   justify-content: center;
 }
+
 .video-thumb {
   width: 54px;
   height: 54px;
@@ -964,14 +859,17 @@ getList()
   color: #909399;
   font-size: 24px;
 }
+
 .video-thumb.small {
   width: 48px;
   height: 48px;
   font-size: 22px;
 }
+
 .asset-meta {
   min-width: 0;
 }
+
 .asset-name {
   font-weight: 500;
   color: #303133;
@@ -980,27 +878,33 @@ getList()
   white-space: nowrap;
   text-overflow: ellipsis;
 }
+
 .asset-sub {
   display: flex;
   align-items: center;
   gap: 8px;
   margin-top: 4px;
 }
+
 .asset-id {
   color: #909399;
   font-size: 12px;
 }
+
 .asset-file-info {
   color: #606266;
   font-size: 13px;
   line-height: 1.6;
 }
+
 .text-muted {
   color: #909399;
 }
+
 .asset-select-tip {
   margin-bottom: 12px;
 }
+
 .form-tip {
   width: 100%;
   margin-top: 4px;
@@ -1008,24 +912,29 @@ getList()
   font-size: 12px;
   line-height: 1.5;
 }
+
 .play-status-alert {
   margin-bottom: 12px;
 }
+
 .plan-preview {
   display: flex;
   flex-direction: column;
   gap: 16px;
 }
+
 .preview-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
 }
+
 .preview-title {
   font-size: 18px;
   font-weight: 600;
   color: #303133;
 }
+
 .preview-sub {
   display: flex;
   align-items: center;
@@ -1034,11 +943,13 @@ getList()
   color: #909399;
   font-size: 13px;
 }
+
 .preview-body {
   display: grid;
   grid-template-columns: minmax(0, 1fr) 320px;
   gap: 16px;
 }
+
 .preview-stage {
   min-height: 420px;
   background: #111827;
@@ -1048,22 +959,27 @@ getList()
   justify-content: center;
   overflow: hidden;
 }
+
 .preview-media {
   max-width: 100%;
   max-height: 420px;
 }
+
 .preview-image {
   object-fit: contain;
 }
+
 .preview-video {
   width: 100%;
   height: 420px;
   background: #000;
 }
+
 .preview-empty {
   color: #cbd5e1;
   font-size: 14px;
 }
+
 .preview-list {
   border: 1px solid #e4e7ed;
   border-radius: 10px;
@@ -1071,11 +987,13 @@ getList()
   max-height: 420px;
   overflow-y: auto;
 }
+
 .preview-list-title {
   font-weight: 600;
   color: #303133;
   margin-bottom: 10px;
 }
+
 .preview-item {
   display: flex;
   align-items: center;
@@ -1084,12 +1002,15 @@ getList()
   border-radius: 8px;
   cursor: pointer;
 }
+
 .preview-item:hover {
   background: #f5f7fa;
 }
+
 .preview-item.active {
   background: #ecf5ff;
 }
+
 .preview-item-index {
   width: 22px;
   height: 22px;
@@ -1101,19 +1022,23 @@ getList()
   justify-content: center;
   font-size: 12px;
 }
+
 .preview-item.active .preview-item-index {
   background: #409eff;
   color: #fff;
 }
+
 .preview-item-thumb {
   width: 44px;
   height: 44px;
   flex-shrink: 0;
 }
+
 .preview-item-meta {
   min-width: 0;
   flex: 1;
 }
+
 .preview-item-name {
   font-size: 13px;
   color: #303133;
@@ -1121,6 +1046,7 @@ getList()
   overflow: hidden;
   text-overflow: ellipsis;
 }
+
 .preview-item-desc {
   display: flex;
   align-items: center;
@@ -1129,14 +1055,17 @@ getList()
   color: #909399;
   font-size: 12px;
 }
+
 .preview-actions {
   display: flex;
   justify-content: center;
   gap: 12px;
 }
+
 .preview-main {
   min-width: 0;
 }
+
 .preview-current-info {
   margin-top: 10px;
   padding: 10px 12px;
@@ -1144,11 +1073,13 @@ getList()
   border-radius: 8px;
   background: #f8fafc;
 }
+
 .preview-current-name {
   font-weight: 600;
   color: #303133;
   margin-bottom: 6px;
 }
+
 .preview-current-desc {
   display: flex;
   align-items: center;