فهرست منبع

卓美马甲功能

yuhongqi 1 روز پیش
والد
کامیت
f631277fdd

+ 9 - 0
src/api/hisStore/storeOrder.js

@@ -94,6 +94,15 @@ export function updateStoreOrder(data) {
   })
 }
 
+// 修改物流编号/发货人手机号
+export function updateDeliveryId(data) {
+  return request({
+    url: '/store/store/storeOrder/editDeliveryId',
+    method: 'put',
+    data: data
+  })
+}
+
 // 删除订单
 export function delStoreOrder(id) {
   return request({

+ 39 - 0
src/api/life/vest.js

@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+
+export function listLifeVest(query) {
+  return request({
+    url: '/life/vest/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getLifeVest(id) {
+  return request({
+    url: '/life/vest/' + id,
+    method: 'get'
+  })
+}
+
+export function addLifeVest(data) {
+  return request({
+    url: '/life/vest',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateLifeVest(data) {
+  return request({
+    url: '/life/vest',
+    method: 'put',
+    data: data
+  })
+}
+
+export function delLifeVest(id) {
+  return request({
+    url: '/life/vest/' + id,
+    method: 'delete'
+  })
+}

+ 55 - 0
src/api/life/video.js

@@ -0,0 +1,55 @@
+import request from '@/utils/request'
+
+export function listLifeVideo(query) {
+  return request({
+    url: '/life/video/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getLifeVideo(videoId) {
+  return request({
+    url: '/life/video/' + videoId,
+    method: 'get'
+  })
+}
+
+export function addLifeVideo(data) {
+  return request({
+    url: '/life/video',
+    method: 'post',
+    data: data
+  })
+}
+
+export function updateLifeVideo(data) {
+  return request({
+    url: '/life/video',
+    method: 'put',
+    data: data
+  })
+}
+
+export function delLifeVideo(videoId) {
+  return request({
+    url: '/life/video/' + videoId,
+    method: 'delete'
+  })
+}
+
+export function shelfLifeVideo(data) {
+  return request({
+    url: '/life/video/shelf',
+    method: 'put',
+    data: data
+  })
+}
+
+export function searchLifeVideoProduct(query) {
+  return request({
+    url: '/life/video/product/search',
+    method: 'get',
+    params: query
+  })
+}

+ 77 - 0
src/views/hisStore/components/productOrder.vue

@@ -36,6 +36,9 @@
           <div class="operate-button-container" v-hasPermi="['store:storeOrder:express']">
             <el-button size="mini" @click="showExpress()">查看物流</el-button>
           </div>
+          <div class="operate-button-container" v-hasPermi="['store:storeOrder:editDeliveryId']">
+            <el-button size="mini" @click="editDelivery()">修改物流</el-button>
+          </div>
         </div>
         <div style="margin: 20px 0px" v-if="order!=null">
         <span class="font-small">
@@ -126,6 +129,11 @@
                   {{ order.deliveryId }}
                 </span>
           </el-descriptions-item>
+          <el-descriptions-item label="发货人手机号">
+                <span v-if="order!=null ">
+                  {{ order.senderPhone }}
+                </span>
+          </el-descriptions-item>
           <el-descriptions-item label="物流状态">
                 <span v-if="order!=null">
                   <el-tag v-for="(item, index) in deliveryStatusOptions"
@@ -689,6 +697,33 @@
         </el-table-column>
       </el-table>
     </el-dialog>
+    <el-dialog :title="editDy.title" :visible.sync="editDy.open" width="600px" append-to-body>
+      <el-form ref="editDyForm" :model="editDyForm" :rules="editDyRules" label-width="120px">
+        <el-form-item label="物流公司" prop="deliverySn">
+          <el-select style="width:220px" v-model="editDyForm.deliverySn" placeholder="请选择" clearable size="small">
+            <el-option key="SF" label="顺丰" value="SF"/>
+            <el-option key="EMS" label="邮政" value="EMS"/>
+            <el-option key="ZTO" label="中通" value="ZTO"/>
+            <el-option key="STO" label="申通" value="STO"/>
+            <el-option key="JD" label="京东" value="JD"/>
+            <el-option key="DBL" label="德邦" value="DBL"/>
+            <el-option key="JTSD" label="极兔" value="JTSD"/>
+            <el-option key="YD" label="韵达" value="YD"/>
+            <el-option key="YTO" label="圆通" value="YTO"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="物流单号" prop="deliveryId">
+          <el-input v-model="editDyForm.deliveryId" placeholder="请输入物流单号"/>
+        </el-form-item>
+        <el-form-item label="发货人手机号" prop="senderPhone">
+          <el-input v-model="editDyForm.senderPhone" placeholder="请输入发货人手机号(物流订阅/查询用)" maxlength="20"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitEditDyForm">确 定</el-button>
+        <el-button @click="editDy.open = false">取 消</el-button>
+      </div>
+    </el-dialog>
     <el-dialog :title="bindCustomerDialog.title" :visible.sync="bindCustomerDialog.open" width="800px" append-to-body>
       <el-form ref="bindCustomerForm" :model="bindCustomerForm" :rules="bindCustomerRules" label-width="100px">
         <el-form-item label="客户查询">
@@ -1053,6 +1088,7 @@ import {
   getStoreOrderAddress,
   updateStoreOrder,
   updateStoreOrderItemJson,
+  updateDeliveryId,
   bindCustomer,
   uploadCredentials,
   getUserPhone,
@@ -1215,6 +1251,24 @@ export default {
         title: "物流信息",
         open: false,
       },
+      editDy: {
+        title: "修改物流单号",
+        open: false,
+      },
+      editDyForm: {
+        id: null,
+        deliverySn: null,
+        deliveryId: null,
+        senderPhone: null,
+      },
+      editDyRules: {
+        deliverySn: [
+          {required: true, message: "物流公司不能为空", trigger: "blur"}
+        ],
+        deliveryId: [
+          {required: true, message: "物流单号不能为空", trigger: "blur"}
+        ],
+      },
       citys: [],
       province: [],
       city: [],
@@ -1825,6 +1879,29 @@ export default {
       });
 
     },
+    editDelivery() {
+      this.editDy.open = true;
+      this.editDyForm.id = this.order.id;
+      this.editDyForm.deliveryId = this.order.deliveryId;
+      this.editDyForm.deliverySn = this.order.deliverySn;
+      this.editDyForm.senderPhone = this.order.senderPhone;
+    },
+    submitEditDyForm() {
+      this.$refs["editDyForm"].validate(valid => {
+        if (valid) {
+          if (this.editDyForm.senderPhone == null) {
+            this.editDyForm.senderPhone = '';
+          }
+          updateDeliveryId(this.editDyForm).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("操作成功");
+              this.editDy.open = false;
+              this.getOrder(this.order.id);
+            }
+          });
+        }
+      });
+    },
     districtChange(val) {
       var item = this.district.find((item) => {
         return item.value == val;

+ 172 - 0
src/views/life/vest/index.vue

@@ -0,0 +1,172 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
+      <el-form-item label="关键字" prop="keyword">
+        <el-input v-model="queryParams.keyword" placeholder="昵称/ID" clearable size="small" @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['life:vest:add']">新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="vestList">
+      <el-table-column label="马甲号ID" align="center" prop="id" width="100"/>
+      <el-table-column label="头像" align="center" width="80">
+        <template slot-scope="scope">
+          <el-image v-if="scope.row.avatar" :src="scope.row.avatar" style="width:40px;height:40px;border-radius:50%" fit="cover"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="昵称" align="center" prop="nickName"/>
+      <el-table-column label="简介" align="center" prop="intro" show-overflow-tooltip/>
+      <el-table-column label="状态" align="center" prop="status" width="80">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.status === 1" type="success">启用</el-tag>
+          <el-tag v-else type="info">禁用</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="220" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['life:vest:edit']">编辑</el-button>
+          <el-button size="mini" type="text" icon="el-icon-video-camera" @click="goVideo(scope.row)" v-hasPermi="['life:video:list']">内容配置</el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['life:vest:remove']">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
+
+    <el-dialog :title="title" :visible.sync="open" width="520px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="90px">
+        <el-form-item label="昵称" prop="nickName">
+          <el-input v-model="form.nickName" placeholder="2-10个字" maxlength="10"/>
+        </el-form-item>
+        <el-form-item label="头像" prop="avatar">
+          <image-upload v-model="form.avatar" :limit="1"/>
+        </el-form-item>
+        <el-form-item label="简介" prop="intro">
+          <el-input v-model="form.intro" type="textarea" :rows="3" placeholder="选填"/>
+        </el-form-item>
+        <el-form-item label="状态" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio :label="1">启用</el-radio>
+            <el-radio :label="0">禁用</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listLifeVest, getLifeVest, addLifeVest, updateLifeVest, delLifeVest } from '@/api/life/vest'
+
+export default {
+  name: 'LifeVest',
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      total: 0,
+      vestList: [],
+      title: '',
+      open: false,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        keyword: null
+      },
+      form: {},
+      rules: {
+        nickName: [
+          { required: true, message: '昵称不能为空', trigger: 'blur' },
+          { min: 2, max: 10, message: '长度需为2-10字', trigger: 'blur' }
+        ],
+        avatar: [{ required: true, message: '头像不能为空', trigger: 'change' }],
+        status: [{ required: true, message: '请选择状态', trigger: 'change' }]
+      }
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    getList() {
+      this.loading = true
+      listLifeVest(this.queryParams).then(res => {
+        this.vestList = res.rows
+        this.total = res.total
+        this.loading = false
+      })
+    },
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    reset() {
+      this.form = {
+        id: null,
+        nickName: null,
+        avatar: null,
+        intro: null,
+        status: 1
+      }
+      this.resetForm('form')
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.resetForm('queryForm')
+      this.handleQuery()
+    },
+    handleAdd() {
+      this.reset()
+      this.open = true
+      this.title = '新增马甲号'
+    },
+    handleUpdate(row) {
+      this.reset()
+      getLifeVest(row.id).then(res => {
+        this.form = res.data
+        this.open = true
+        this.title = '编辑马甲号'
+      })
+    },
+    submitForm() {
+      this.$refs['form'].validate(valid => {
+        if (!valid) return
+        const req = this.form.id != null ? updateLifeVest(this.form) : addLifeVest(this.form)
+        req.then(() => {
+          this.msgSuccess('操作成功')
+          this.open = false
+          this.getList()
+        })
+      })
+    },
+    handleDelete(row) {
+      this.$confirm('确认删除马甲号「' + row.nickName + '」吗?', '警告', { type: 'warning' }).then(() => {
+        return delLifeVest(row.id)
+      }).then(() => {
+        this.getList()
+        this.msgSuccess('删除成功')
+      }).catch(() => {})
+    },
+    goVideo(row) {
+      this.$router.push({ path: '/live/life/video', query: { vestId: row.id } })
+    }
+  }
+}
+</script>

+ 327 - 0
src/views/life/video/index.vue

@@ -0,0 +1,327 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="90px">
+      <el-form-item label="马甲号" prop="vestId">
+        <el-select v-model="queryParams.vestId" filterable clearable placeholder="全部马甲" size="small" style="width:180px">
+          <el-option v-for="v in vestOptions" :key="v.id" :label="v.nickName + '(' + v.id + ')'" :value="v.id"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="关键字" prop="keyword">
+        <el-input v-model="queryParams.keyword" placeholder="标题/ID/文件名" clearable size="small" @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="上架" prop="isShow">
+        <el-select v-model="queryParams.isShow" clearable placeholder="全部" size="small" style="width:100px">
+          <el-option label="上架" :value="1"/>
+          <el-option label="下架" :value="0"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['life:video:add']">新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="videoList">
+      <el-table-column label="视频ID" align="center" prop="videoId" width="90"/>
+      <el-table-column label="封面" align="center" width="80">
+        <template slot-scope="scope">
+          <el-image v-if="scope.row.coverUrl" :src="scope.row.coverUrl" style="width:50px;height:50px" fit="cover"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="标题" align="center" prop="title" show-overflow-tooltip/>
+      <el-table-column label="文件名" align="center" prop="fileName" show-overflow-tooltip/>
+      <el-table-column label="马甲ID" align="center" prop="vestId" width="80"/>
+      <el-table-column label="时长(秒)" align="center" prop="duration" width="90"/>
+      <el-table-column label="上架" align="center" width="80">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.isShow === 1" type="success">是</el-tag>
+          <el-tag v-else type="info">否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="主推" align="center" width="80">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.isHot === 1" type="warning">是</el-tag>
+          <span v-else>否</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="播放/赞/藏/享" align="center" width="140">
+        <template slot-scope="scope">
+          {{ scope.row.views || 0 }}/{{ scope.row.likes || 0 }}/{{ scope.row.favoriteNum || 0 }}/{{ scope.row.shareNum || 0 }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" @click="handleUpdate(scope.row)" v-hasPermi="['life:video:edit']">修改</el-button>
+          <el-button size="mini" type="text" @click="handleShelf(scope.row)" v-hasPermi="['life:video:edit']">
+            {{ scope.row.isShow === 1 ? '下架' : '上架' }}
+          </el-button>
+          <el-button size="mini" type="text" @click="handleDelete(scope.row)" v-hasPermi="['life:video:remove']">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
+
+    <el-dialog :title="title" :visible.sync="open" width="720px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="110px">
+        <el-form-item label="马甲号" prop="vestId">
+          <el-select v-model="form.vestId" filterable placeholder="请选择马甲号" style="width:100%">
+            <el-option v-for="v in vestOptions" :key="v.id" :label="v.nickName + '(' + v.id + ')'" :value="v.id"/>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="视频标题" prop="title">
+          <el-input v-model="form.title" maxlength="20" show-word-limit placeholder="不超过20字"/>
+        </el-form-item>
+        <el-form-item label="文件名称" prop="fileName">
+          <el-input v-model="form.fileName" placeholder="上传文件名或标识"/>
+        </el-form-item>
+        <el-form-item label="封面" prop="coverUrl">
+          <image-upload v-model="form.coverUrl" :limit="1"/>
+        </el-form-item>
+        <el-form-item label="视频地址" prop="videoUrl">
+          <el-input v-model="form.videoUrl" placeholder="mp4 地址(素材库上传后粘贴)"/>
+        </el-form-item>
+        <el-form-item label="时长(秒)" prop="duration">
+          <el-input-number v-model="form.duration" :min="0" :controls="false"/>
+        </el-form-item>
+        <el-form-item label="是否上架" prop="isShow">
+          <el-radio-group v-model="form.isShow">
+            <el-radio :label="1">上架</el-radio>
+            <el-radio :label="0">下架</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="今日主推" prop="isHot">
+          <el-radio-group v-model="form.isHot">
+            <el-radio :label="1">是</el-radio>
+            <el-radio :label="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="关联商品">
+          <el-button type="primary" size="mini" @click="openProductDialog">选择商品</el-button>
+          <el-table :data="selectedProducts" border size="mini" style="margin-top:8px">
+            <el-table-column label="商品ID" prop="productId" width="90"/>
+            <el-table-column label="名称" prop="productName" show-overflow-tooltip/>
+            <el-table-column label="价格" prop="price" width="90"/>
+            <el-table-column label="库存" prop="stock" width="80"/>
+            <el-table-column label="操作" width="80">
+              <template slot-scope="scope">
+                <el-button type="text" size="mini" @click="removeProduct(scope.$index)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog title="选择商品" :visible.sync="productOpen" width="780px" append-to-body>
+      <el-form :inline="true" size="small">
+        <el-form-item label="商品名称">
+          <el-input v-model="productQuery.productName" clearable @keyup.enter.native="searchProduct"/>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" size="mini" @click="searchProduct">搜索</el-button>
+        </el-form-item>
+      </el-form>
+      <el-table :data="productList" border v-loading="productLoading" max-height="400">
+        <el-table-column label="ID" prop="productId" width="80"/>
+        <el-table-column label="图片" width="70">
+          <template slot-scope="scope">
+            <el-image v-if="scope.row.image" :src="scope.row.image" style="width:40px;height:40px" fit="cover"/>
+          </template>
+        </el-table-column>
+        <el-table-column label="名称" prop="productName" show-overflow-tooltip/>
+        <el-table-column label="价格" prop="price" width="90"/>
+        <el-table-column label="库存" prop="stock" width="80"/>
+        <el-table-column label="操作" width="100">
+          <template slot-scope="scope">
+            <el-button type="text" size="mini" :disabled="isSelected(scope.row.productId)" @click="pickProduct(scope.row)">
+              {{ isSelected(scope.row.productId) ? '已选' : '选择' }}
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="productTotal>0" :total="productTotal" :page.sync="productQuery.pageNum" :limit.sync="productQuery.pageSize" @pagination="searchProduct"/>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listLifeVest } from '@/api/life/vest'
+import { listLifeVideo, getLifeVideo, addLifeVideo, updateLifeVideo, delLifeVideo, shelfLifeVideo, searchLifeVideoProduct } from '@/api/life/video'
+
+export default {
+  name: 'LifeVideo',
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      total: 0,
+      videoList: [],
+      vestOptions: [],
+      title: '',
+      open: false,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        vestId: null,
+        keyword: null,
+        isShow: null
+      },
+      form: {},
+      rules: {
+        vestId: [{ required: true, message: '请选择马甲号', trigger: 'change' }],
+        title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
+        coverUrl: [{ required: true, message: '封面不能为空', trigger: 'change' }],
+        videoUrl: [{ required: true, message: '视频地址不能为空', trigger: 'blur' }],
+        isShow: [{ required: true, message: '请选择是否上架', trigger: 'change' }]
+      },
+      selectedProducts: [],
+      productOpen: false,
+      productLoading: false,
+      productList: [],
+      productTotal: 0,
+      productQuery: {
+        pageNum: 1,
+        pageSize: 10,
+        productName: null
+      }
+    }
+  },
+  created() {
+    if (this.$route.query.vestId) {
+      this.queryParams.vestId = Number(this.$route.query.vestId)
+    }
+    this.loadVests()
+    this.getList()
+  },
+  methods: {
+    loadVests() {
+      listLifeVest({ pageNum: 1, pageSize: 500 }).then(res => {
+        this.vestOptions = res.rows || []
+      })
+    },
+    getList() {
+      this.loading = true
+      listLifeVideo(this.queryParams).then(res => {
+        this.videoList = res.rows
+        this.total = res.total
+        this.loading = false
+      })
+    },
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    reset() {
+      this.form = {
+        videoId: null,
+        vestId: this.queryParams.vestId || null,
+        title: null,
+        fileName: null,
+        coverUrl: null,
+        videoUrl: null,
+        duration: 0,
+        isShow: 1,
+        isHot: 0,
+        productIds: []
+      }
+      this.selectedProducts = []
+      this.resetForm('form')
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.resetForm('queryForm')
+      this.queryParams.vestId = null
+      this.handleQuery()
+    },
+    handleAdd() {
+      this.reset()
+      this.open = true
+      this.title = '新增生活号视频'
+    },
+    handleUpdate(row) {
+      this.reset()
+      getLifeVideo(row.videoId).then(res => {
+        this.form = Object.assign({}, res.data)
+        const ids = res.data.productIds || []
+        this.form.productIds = ids
+        this.selectedProducts = ids.map(id => ({ productId: id, productName: '商品#' + id, price: '-', stock: '-' }))
+        // 回填商品详情
+        if (ids.length) {
+          searchLifeVideoProduct({ pageNum: 1, pageSize: 100 }).then(p => {
+            const map = {}
+            ;(p.rows || []).forEach(item => { map[item.productId] = item })
+            this.selectedProducts = ids.map(id => map[id] || { productId: id, productName: '商品#' + id })
+          })
+        }
+        this.open = true
+        this.title = '修改生活号视频'
+      })
+    },
+    submitForm() {
+      this.$refs['form'].validate(valid => {
+        if (!valid) return
+        this.form.productIds = this.selectedProducts.map(p => p.productId)
+        const req = this.form.videoId != null ? updateLifeVideo(this.form) : addLifeVideo(this.form)
+        req.then(() => {
+          this.msgSuccess('操作成功')
+          this.open = false
+          this.getList()
+        })
+      })
+    },
+    handleShelf(row) {
+      const next = row.isShow === 1 ? 0 : 1
+      shelfLifeVideo({ videoId: row.videoId, isShow: next }).then(() => {
+        this.msgSuccess('操作成功')
+        this.getList()
+      })
+    },
+    handleDelete(row) {
+      this.$confirm('删除后不可恢复,确认删除该视频?', '警告', { type: 'warning' }).then(() => {
+        return delLifeVideo(row.videoId)
+      }).then(() => {
+        this.getList()
+        this.msgSuccess('删除成功')
+      }).catch(() => {})
+    },
+    openProductDialog() {
+      this.productOpen = true
+      this.searchProduct()
+    },
+    searchProduct() {
+      this.productLoading = true
+      searchLifeVideoProduct(this.productQuery).then(res => {
+        this.productList = res.rows || []
+        this.productTotal = res.total || 0
+        this.productLoading = false
+      }).catch(() => { this.productLoading = false })
+    },
+    isSelected(productId) {
+      return this.selectedProducts.some(p => p.productId === productId)
+    },
+    pickProduct(row) {
+      if (!this.isSelected(row.productId)) {
+        this.selectedProducts.push(row)
+      }
+    },
+    removeProduct(index) {
+      this.selectedProducts.splice(index, 1)
+    }
+  }
+}
+</script>

+ 34 - 2
src/views/live/live/index.vue

@@ -410,6 +410,23 @@
                         <el-radio :label="2">下架</el-radio>
                     </el-radio-group>
                 </el-form-item>
+                <el-form-item label="可见范围" prop="visibleScope">
+                    <el-radio-group v-model="form.visibleScope" :disabled="isViewOnly">
+                        <el-radio :label="0">私域直播</el-radio>
+                        <el-radio :label="1">公域直播</el-radio>
+                    </el-radio-group>
+                </el-form-item>
+                <el-form-item label="直播马甲号" prop="vestId">
+                    <el-select v-model="form.vestId" filterable clearable placeholder="请选择马甲号" style="width:100%" :disabled="isViewOnly">
+                        <el-option
+                            v-for="item in lifeVestOptions"
+                            :key="item.id"
+                            :label="item.nickName + '(' + item.id + ')'"
+                            :value="item.id"
+                        />
+                    </el-select>
+                    <div style="color:#909399;font-size:12px;line-height:1.4;margin-top:4px">公域直播必须绑定马甲号;私域可不选</div>
+                </el-form-item>
             </el-form>
             <div slot="footer" class="dialog-footer">
                 <el-button type="primary" @click="submitForm" v-show="!isViewOnly">确 定</el-button>
@@ -480,6 +497,7 @@ import user from '@/store/modules/user';
 import VideoUpload from "@/components/LiveVideoUpload/single.vue";
 import {listTag} from "@/api/qw/tag";
 import {getAppAllLint, getAppAllList} from "../../../api/live/live";
+import { listLifeVest } from "@/api/life/vest";
 
 export default {
     name: "Live",
@@ -600,11 +618,13 @@ export default {
             currentQrcodeUrl: "",        // 当前要展示的二维码地址
             defaultImg: "https://via.placeholder.com/400x400?text=二维码加载失败",  // 占位图
             // 导出评论状态:key为liveId,value为是否正在导出
-            exportingComments: {}
+            exportingComments: {},
+            lifeVestOptions: []
         };
     },
     created() {
         this.getList();
+        this.loadLifeVestOptions();
         // 新增:获取直播类型字典
         this.getDicts("live_type").then((response) => {
             this.liveTypeDictList = response.data;
@@ -878,7 +898,9 @@ export default {
                 isShow: 1,
                 isTranscode:0,
                 uploadType:1,
-                transcodeFileKey:null
+                transcodeFileKey:null,
+                visibleScope: 0,
+                vestId: null
             };
             this.videoUrl = "";
             this.resetForm("form");
@@ -970,6 +992,9 @@ export default {
         submitForm() {
             if(this.form.liveId != null) { this.videoUrl = this.form.videoUrl; }
             if(this.form.liveType==2 && this.videoUrl.length == 0) {return this.$message.error("请上传视频");}
+            if (this.form.visibleScope === 1 && !this.form.vestId) {
+                return this.$message.error("公域直播必须绑定马甲号");
+            }
             this.$refs["form"].validate(valid => {
                 if (valid) {
                     this.form.videoUrl = this.videoUrl;
@@ -989,6 +1014,13 @@ export default {
                 }
             });
         },
+        loadLifeVestOptions() {
+            listLifeVest({ pageNum: 1, pageSize: 500 }).then(res => {
+                this.lifeVestOptions = res.rows || [];
+            }).catch(() => {
+                this.lifeVestOptions = [];
+            });
+        },
         /** 删除按钮操作 */
         handleDelete(row) {
             const liveIds = row.liveId || this.ids;