Browse Source

飞书前端

xw 3 tuần trước cách đây
mục cha
commit
5bd677be29

+ 227 - 0
src/api/feishu/feishuAccount.js

@@ -0,0 +1,227 @@
+import request from '@/utils/request'
+
+
+
+export function listMyFeishuAccount(query) {
+
+  return request({
+
+    url: '/feishu/account/my/list',
+
+    method: 'get',
+
+    params: query
+
+  })
+
+}
+
+
+
+export function listCompanyFeishuAccount(query) {
+
+  return request({
+
+    url: '/feishu/account/company/list',
+
+    method: 'get',
+
+    params: query
+
+  })
+
+}
+
+
+
+export function listFeishuAccount(query) {
+
+  return listMyFeishuAccount(query)
+
+}
+
+
+
+export function listFeishuAccountOptions(query) {
+
+  return request({
+
+    url: '/feishu/account/options',
+
+    method: 'get',
+
+    params: query
+
+  })
+
+}
+
+
+
+export function getFeishuAccount(id) {
+
+  return request({
+
+    url: '/feishu/account/' + id,
+
+    method: 'get'
+
+  })
+
+}
+
+
+
+export function addMyFeishuAccount(data) {
+
+  return request({
+
+    url: '/feishu/account/my',
+
+    method: 'post',
+
+    data: data
+
+  })
+
+}
+
+
+
+export function addMyFeishuAccountBatch(data) {
+
+  return request({
+
+    url: '/feishu/account/my/batch',
+
+    method: 'post',
+
+    data: data
+
+  })
+
+}
+
+
+
+export function updateMyFeishuAccount(data) {
+
+  return request({
+
+    url: '/feishu/account/my',
+
+    method: 'put',
+
+    data: data
+
+  })
+
+}
+
+
+
+export function delMyFeishuAccount(id) {
+
+  return request({
+
+    url: '/feishu/account/my/' + id,
+
+    method: 'delete'
+
+  })
+
+}
+
+
+
+export function addCompanyFeishuAccount(data) {
+
+  return request({
+
+    url: '/feishu/account/company',
+
+    method: 'post',
+
+    data: data
+
+  })
+
+}
+
+
+
+export function addCompanyFeishuAccountBatch(data) {
+
+  return request({
+
+    url: '/feishu/account/company/batch',
+
+    method: 'post',
+
+    data: data
+
+  })
+
+}
+
+
+
+export function updateCompanyFeishuAccount(data) {
+
+  return request({
+
+    url: '/feishu/account/company',
+
+    method: 'put',
+
+    data: data
+
+  })
+
+}
+
+
+
+export function delCompanyFeishuAccount(id) {
+
+  return request({
+
+    url: '/feishu/account/company/' + id,
+
+    method: 'delete'
+
+  })
+
+}
+
+
+
+/** @deprecated ??? addMyFeishuAccount */
+
+export function addFeishuAccount(data) {
+
+  return addMyFeishuAccount(data)
+
+}
+
+
+
+/** @deprecated ??? updateMyFeishuAccount */
+
+export function updateFeishuAccount(data) {
+
+  return updateMyFeishuAccount(data)
+
+}
+
+
+
+/** @deprecated ??? delMyFeishuAccount */
+
+export function delFeishuAccount(id) {
+
+  return delMyFeishuAccount(id)
+
+}
+
+

+ 362 - 0
src/views/feishu/feishuAccount/index.vue

@@ -0,0 +1,362 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" size="small" label-width="90px">
+      <el-form-item label="销售昵称">
+        <el-input v-model="queryParams.nickName" placeholder="销售昵称" clearable @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="手机号">
+        <el-input v-model="queryParams.phone" placeholder="联系人手机号" clearable @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="AppId">
+        <el-input v-model="queryParams.appId" placeholder="AppId" clearable @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="状态">
+        <el-select v-model="queryParams.status" placeholder="全部" clearable>
+          <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" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" @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">新增飞书账号</el-button>
+      </el-col>
+    </el-row>
+
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="ID" prop="id" width="70" align="center"/>
+      <el-table-column label="销售昵称" min-width="120" show-overflow-tooltip>
+        <template slot-scope="scope">
+          {{ scope.row.nickName || scope.row.userName || '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="联系人手机号" prop="phone" min-width="120" show-overflow-tooltip/>
+      <el-table-column label="AppId" prop="appId" min-width="180" show-overflow-tooltip/>
+      <el-table-column label="状态" width="90" align="center">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'" size="mini">
+            {{ scope.row.status === 1 ? '启用' : '禁用' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="已使用次数" prop="numberUse" width="100" align="center"/>
+      <el-table-column label="错误信息" prop="errorMsg" min-width="140" show-overflow-tooltip/>
+      <el-table-column label="更新时间" prop="updateTime" width="160" align="center"/>
+      <el-table-column label="操作" width="180" align="center" fixed="right">
+        <template slot-scope="scope">
+          <el-button type="text" size="mini" @click="handleAssign(scope.row)">分配销售</el-button>
+          <el-button type="text" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
+          <el-button type="text" size="mini" class="danger-text" @click="handleDelete(scope.row)">删除</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="isBatchAdd ? '860px' : '560px'" append-to-body>
+      <el-form ref="form" :model="form" :rules="formRules" label-width="110px">
+        <template v-if="isBatchAdd">
+          <el-form-item label="所属销售人员" prop="companyUserId">
+            <el-select v-model="form.companyUserId" placeholder="请选择销售" filterable style="width: 100%;">
+              <el-option
+                v-for="user in salesOptions"
+                :key="user.userId"
+                :label="user.nickName || user.userName"
+                :value="user.userId"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="状态">
+            <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-item label="账号列表" required>
+            <el-table :data="accountRows" border size="small" class="account-table">
+              <el-table-column label="序号" type="index" width="55" align="center"/>
+              <el-table-column label="联系人手机号" min-width="150">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.phone" placeholder="选填"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="AppId" min-width="180">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.appId" placeholder="应用 AppId"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="AppSecret" min-width="180">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.appSecret" type="password" show-password placeholder="应用 AppSecret"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" width="120" align="center">
+                <template slot-scope="scope">
+                  <el-button type="text" size="mini" @click="addAccountRow">新增</el-button>
+                  <el-button
+                    v-if="accountRows.length > 1"
+                    type="text"
+                    size="mini"
+                    class="danger-text"
+                    @click="removeAccountRow(scope.$index)"
+                  >删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-form-item>
+        </template>
+        <template v-else>
+          <el-form-item label="所属销售人员" prop="companyUserId">
+            <el-select v-model="form.companyUserId" placeholder="请选择销售" filterable style="width: 100%;">
+              <el-option
+                v-for="user in salesOptions"
+                :key="user.userId"
+                :label="user.nickName || user.userName"
+                :value="user.userId"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="联系人手机号" prop="phone">
+            <el-input v-model="form.phone" placeholder="可选填写联系人手机号"/>
+          </el-form-item>
+          <el-form-item label="AppId" prop="appId">
+            <el-input v-model="form.appId" placeholder="应用 AppId"/>
+          </el-form-item>
+          <el-form-item v-if="!assignOnly" label="AppSecret" prop="appSecret">
+            <el-input v-model="form.appSecret" type="password" show-password :placeholder="form.id ? '不修改可留空' : '应用 AppSecret'"/>
+          </el-form-item>
+          <el-form-item v-if="!assignOnly" 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>
+        </template>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="open = false">取消</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  listCompanyFeishuAccount,
+  getFeishuAccount,
+  addCompanyFeishuAccount,
+  addCompanyFeishuAccountBatch,
+  updateCompanyFeishuAccount,
+  delCompanyFeishuAccount
+} from '@/api/feishu/feishuAccount'
+import { listUser } from '@/api/company/companyUser'
+
+export default {
+  name: 'FeishuAccount',
+  data() {
+    return {
+      loading: false,
+      submitLoading: false,
+      total: 0,
+      list: [],
+      salesOptions: [],
+      title: '',
+      open: false,
+      assignOnly: false,
+      accountRows: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        nickName: null,
+        phone: null,
+        appId: null,
+        status: null
+      },
+      form: {},
+      baseRules: {
+        companyUserId: [{ required: true, message: '请选择销售人员', trigger: 'change' }],
+        appId: [{ required: true, message: 'AppId不能为空', trigger: 'blur' }],
+        appSecret: [{ required: true, message: 'AppSecret不能为空', trigger: 'blur' }]
+      }
+    }
+  },
+  computed: {
+    isBatchAdd() {
+      return !this.form.id && !this.assignOnly
+    },
+    formRules() {
+      if (this.isBatchAdd) {
+        return { companyUserId: this.baseRules.companyUserId }
+      }
+      if (this.form.id && !this.assignOnly) {
+        return {
+          companyUserId: this.baseRules.companyUserId,
+          appId: this.baseRules.appId,
+          appSecret: []
+        }
+      }
+      if (this.assignOnly) {
+        return { companyUserId: this.baseRules.companyUserId }
+      }
+      return this.baseRules
+    }
+  },
+  created() {
+    this.getList()
+    this.loadSalesOptions()
+  },
+  methods: {
+    createAccountRow() {
+      return { phone: null, appId: null, appSecret: null }
+    },
+    resetAccountRows() {
+      this.accountRows = [this.createAccountRow()]
+    },
+    addAccountRow() {
+      this.accountRows.push(this.createAccountRow())
+    },
+    removeAccountRow(index) {
+      this.accountRows.splice(index, 1)
+    },
+    loadSalesOptions() {
+      listUser({ pageNum: 1, pageSize: 500, status: '0' }).then(res => {
+        this.salesOptions = res.rows || []
+      })
+    },
+    getList() {
+      this.loading = true
+      listCompanyFeishuAccount(this.queryParams).then(res => {
+        this.list = res.rows || []
+        this.total = res.total || 0
+        this.loading = false
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.resetForm('queryForm')
+      this.queryParams.status = null
+      this.handleQuery()
+    },
+    resetFormData() {
+      this.form = {
+        id: null,
+        companyUserId: null,
+        phone: null,
+        appId: null,
+        appSecret: null,
+        status: 1
+      }
+      this.resetAccountRows()
+    },
+    validateAccountRows() {
+      for (let i = 0; i < this.accountRows.length; i++) {
+        const row = this.accountRows[i]
+        if (!row.appId || !row.appId.trim()) {
+          this.$message.warning(`第 ${i + 1} 行 AppId 不能为空`)
+          return false
+        }
+        if (!row.appSecret || !row.appSecret.trim()) {
+          this.$message.warning(`第 ${i + 1} 行 AppSecret 不能为空`)
+          return false
+        }
+      }
+      return true
+    },
+    handleAdd() {
+      this.assignOnly = false
+      this.resetFormData()
+      this.open = true
+      this.title = '新增飞书账号(支持批量)'
+      this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
+    },
+    handleUpdate(row) {
+      this.assignOnly = false
+      getFeishuAccount(row.id).then(res => {
+        this.form = Object.assign({}, res.data, { appSecret: '' })
+        this.open = true
+        this.title = '编辑飞书账号'
+      })
+    },
+    handleAssign(row) {
+      this.assignOnly = true
+      this.form = {
+        id: row.id,
+        companyUserId: row.companyUserId,
+        phone: row.phone,
+        appId: row.appId,
+        appSecret: '',
+        status: row.status
+      }
+      this.open = true
+      this.title = '重新分配账号所属销售'
+      this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
+    },
+    submitForm() {
+      this.$refs.form.validate(valid => {
+        if (!valid) return
+        if (this.isBatchAdd) {
+          if (!this.validateAccountRows()) return
+          this.submitLoading = true
+          const payload = this.accountRows.map(row => ({
+            companyUserId: this.form.companyUserId,
+            phone: row.phone,
+            appId: row.appId.trim(),
+            appSecret: row.appSecret.trim(),
+            status: this.form.status
+          }))
+          addCompanyFeishuAccountBatch(payload).then(() => {
+            this.$message.success(`成功新增 ${payload.length} 条账号`)
+            this.open = false
+            this.getList()
+          }).finally(() => {
+            this.submitLoading = false
+          })
+          return
+        }
+        const req = this.form.id
+          ? updateCompanyFeishuAccount(this.form)
+          : addCompanyFeishuAccount(this.form)
+        req.then(() => {
+          this.$message.success('操作成功')
+          this.open = false
+          this.getList()
+        })
+      })
+    },
+    handleDelete(row) {
+      this.$confirm('确定删除该账号?', '提示', { type: 'warning' }).then(() => {
+        return delCompanyFeishuAccount(row.id)
+      }).then(() => {
+        this.$message.success('删除成功')
+        this.getList()
+      }).catch(() => {})
+    }
+  }
+}
+</script>
+
+<style scoped>
+.danger-text {
+  color: #f56c6c;
+}
+.account-table {
+  width: 100%;
+}
+</style>

+ 295 - 0
src/views/feishu/myFeishuAccount/index.vue

@@ -0,0 +1,295 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" size="small" label-width="100px">
+      <el-form-item label="联系人手机号">
+        <el-input v-model="queryParams.phone" placeholder="手机号/备注" clearable @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="AppId">
+        <el-input v-model="queryParams.appId" placeholder="AppId" clearable @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="状态">
+        <el-select v-model="queryParams.status" placeholder="全部" clearable>
+          <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" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" @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">新增</el-button>
+      </el-col>
+    </el-row>
+
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="ID" prop="id" width="70" align="center"/>
+      <el-table-column label="联系人手机号" prop="phone" min-width="120" show-overflow-tooltip/>
+      <el-table-column label="AppId" prop="appId" min-width="180" show-overflow-tooltip/>
+      <el-table-column label="状态" width="90" align="center">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'" size="mini">
+            {{ scope.row.status === 1 ? '启用' : '禁用' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="已使用次数" prop="numberUse" width="100" align="center"/>
+      <el-table-column label="错误信息" prop="errorMsg" min-width="160" show-overflow-tooltip/>
+      <el-table-column label="更新时间" prop="updateTime" width="160" align="center"/>
+      <el-table-column label="操作" width="160" align="center" fixed="right">
+        <template slot-scope="scope">
+          <el-button type="text" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
+          <el-button type="text" size="mini" class="danger-text" @click="handleDelete(scope.row)">删除</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="isBatchAdd ? '820px' : '520px'" append-to-body>
+      <el-form ref="form" :model="form" :rules="formRules" label-width="110px">
+        <template v-if="isBatchAdd">
+          <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-item label="账号列表" required>
+            <el-table :data="accountRows" border size="small" class="account-table">
+              <el-table-column label="序号" type="index" width="55" align="center"/>
+              <el-table-column label="联系人手机号" min-width="150">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.phone" placeholder="选填"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="AppId" min-width="180">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.appId" placeholder="应用 AppId"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="AppSecret" min-width="180">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.appSecret" type="password" show-password placeholder="应用 AppSecret"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" width="120" align="center">
+                <template slot-scope="scope">
+                  <el-button type="text" size="mini" @click="addAccountRow">新增</el-button>
+                  <el-button
+                    v-if="accountRows.length > 1"
+                    type="text"
+                    size="mini"
+                    class="danger-text"
+                    @click="removeAccountRow(scope.$index)"
+                  >删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-form-item>
+        </template>
+        <template v-else>
+          <el-form-item label="联系人手机号" prop="phone">
+            <el-input v-model="form.phone" placeholder="可选填写联系人手机号"/>
+          </el-form-item>
+          <el-form-item label="AppId" prop="appId">
+            <el-input v-model="form.appId" placeholder="应用 AppId"/>
+          </el-form-item>
+          <el-form-item label="AppSecret" prop="appSecret">
+            <el-input v-model="form.appSecret" type="password" show-password :placeholder="form.id ? '不修改可留空' : '应用 AppSecret'"/>
+          </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>
+        </template>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="open = false">取消</el-button>
+        <el-button type="primary" :loading="submitLoading" @click="submitForm">确定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  listMyFeishuAccount,
+  getFeishuAccount,
+  addMyFeishuAccount,
+  addMyFeishuAccountBatch,
+  updateMyFeishuAccount,
+  delMyFeishuAccount
+} from '@/api/feishu/feishuAccount'
+
+export default {
+  name: 'MyFeishuAccount',
+  data() {
+    return {
+      loading: false,
+      submitLoading: false,
+      total: 0,
+      list: [],
+      title: '',
+      open: false,
+      accountRows: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        phone: null,
+        appId: null,
+        status: null
+      },
+      form: {},
+      baseRules: {
+        appId: [{ required: true, message: 'AppId不能为空', trigger: 'blur' }],
+        appSecret: [{ required: true, message: 'AppSecret不能为空', trigger: 'blur' }]
+      }
+    }
+  },
+  computed: {
+    isBatchAdd() {
+      return !this.form.id
+    },
+    formRules() {
+      if (this.isBatchAdd) {
+        return {}
+      }
+      if (this.form.id) {
+        return { appId: this.baseRules.appId, appSecret: [] }
+      }
+      return this.baseRules
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    createAccountRow() {
+      return { phone: null, appId: null, appSecret: null }
+    },
+    resetAccountRows() {
+      this.accountRows = [this.createAccountRow()]
+    },
+    addAccountRow() {
+      this.accountRows.push(this.createAccountRow())
+    },
+    removeAccountRow(index) {
+      this.accountRows.splice(index, 1)
+    },
+    getList() {
+      this.loading = true
+      listMyFeishuAccount(this.queryParams).then(res => {
+        this.list = res.rows || []
+        this.total = res.total || 0
+        this.loading = false
+      }).catch(() => {
+        this.loading = false
+      })
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+    resetQuery() {
+      this.resetForm('queryForm')
+      this.queryParams.status = null
+      this.handleQuery()
+    },
+    resetFormData() {
+      this.form = {
+        id: null,
+        phone: null,
+        appId: null,
+        appSecret: null,
+        status: 1
+      }
+      this.resetAccountRows()
+    },
+    validateAccountRows() {
+      for (let i = 0; i < this.accountRows.length; i++) {
+        const row = this.accountRows[i]
+        if (!row.appId || !row.appId.trim()) {
+          this.$message.warning(`第 ${i + 1} 行 AppId 不能为空`)
+          return false
+        }
+        if (!row.appSecret || !row.appSecret.trim()) {
+          this.$message.warning(`第 ${i + 1} 行 AppSecret 不能为空`)
+          return false
+        }
+      }
+      return true
+    },
+    handleAdd() {
+      this.resetFormData()
+      this.open = true
+      this.title = '新增我的飞书账号(支持批量)'
+      this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
+    },
+    handleUpdate(row) {
+      getFeishuAccount(row.id).then(res => {
+        this.form = Object.assign({}, res.data, { appSecret: '' })
+        this.open = true
+        this.title = '编辑我的飞书账号'
+      })
+    },
+    submitForm() {
+      this.$refs.form.validate(valid => {
+        if (!valid) return
+        if (this.isBatchAdd) {
+          if (!this.validateAccountRows()) return
+          this.submitLoading = true
+          const payload = this.accountRows.map(row => ({
+            phone: row.phone,
+            appId: row.appId.trim(),
+            appSecret: row.appSecret.trim(),
+            status: this.form.status
+          }))
+          addMyFeishuAccountBatch(payload).then(() => {
+            this.$message.success(`成功新增 ${payload.length} 条账号`)
+            this.open = false
+            this.getList()
+          }).finally(() => {
+            this.submitLoading = false
+          })
+          return
+        }
+        const req = this.form.id ? updateMyFeishuAccount(this.form) : addMyFeishuAccount(this.form)
+        req.then(() => {
+          this.$message.success('操作成功')
+          this.open = false
+          this.getList()
+        })
+      })
+    },
+    handleDelete(row) {
+      this.$confirm('确定删除该账号?', '提示', { type: 'warning' }).then(() => {
+        return delMyFeishuAccount(row.id)
+      }).then(() => {
+        this.$message.success('删除成功')
+        this.getList()
+      }).catch(() => {})
+    }
+  }
+}
+</script>
+
+<style scoped>
+.danger-text {
+  color: #f56c6c;
+}
+.account-table {
+  width: 100%;
+}
+</style>

+ 2 - 1
src/views/qw/sopLogs/sopLogsList.vue

@@ -177,6 +177,7 @@
                 <span v-if="item.contentType == 7">语音</span>
                 <span v-if="item.contentType == 8">视频号</span>
                 <span v-if="item.contentType == 9">APP</span>
+                <span v-if="item.contentType == 17">飞书</span>
                 <span v-if="item.contentType == 10">自定义小程序</span>
                 <span v-if="item.contentType == 11">群公告</span>
                 <span v-if="item.contentType == 4"><el-button size="mini" type="primary" @click="generateShortLink(item)" style="margin-left: 330px;">生成短链</el-button></span>
@@ -196,7 +197,7 @@
                 fit="contain"
                 @click.prevent="openImageViewer(item.imgUrl)" />
             </div>
-            <div v-if="item.contentType == 3 || item.contentType == 9" class="message-style">
+            <div v-if="item.contentType == 3 || item.contentType == 9 || item.contentType == 17" class="message-style">
               <div
                 style="background-color: white; padding: 10px; width: 100%"
                 @click="openLink(item.linkUrl)">

+ 25 - 16
src/views/qw/sopTemp/updateSopTemp.vue

@@ -303,7 +303,7 @@
                                               <el-radio
                                                 :key="item.dictValue"
                                                 :label="item.dictValue"
-                                                :disabled="(content.type!=2 && item.dictValue === '9') || (content.isOfficial==1 && ['5','6','7','8','9'].includes(item.dictValue))"
+                                                :disabled="(content.type!=2 && (item.dictValue === '9' || item.dictValue === '17')) || (content.isOfficial==1 && ['5','6','7','8','9','17'].includes(item.dictValue))"
                                                 v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
                                               </el-radio>
                                             </el-radio-group>
@@ -315,7 +315,7 @@
                                               <el-radio
                                                 :key="item.dictValue"
                                                 :label="item.dictValue"
-                                                :disabled="((content.type!=2 || form.sendType == 4) && (item.dictValue === '8' || item.dictValue === '9') || (content.isOfficial==1 && ['5','6','7','8','9'].includes(item.dictValue)))"
+                                                :disabled="((content.type!=2 || form.sendType == 4) && (item.dictValue === '8' || item.dictValue === '9' || item.dictValue === '17') || (content.isOfficial==1 && ['5','6','7','8','9','17'].includes(item.dictValue)))"
                                                 v-for="item in sysQwSopAiContentType">{{ item.dictLabel }}
                                               </el-radio>
                                             </el-radio-group>
@@ -327,7 +327,7 @@
                                               <el-radio
                                                 :key="item.dictValue"
                                                 :label="item.dictValue"
-                                                :disabled="(content.type!=2 && item.dictValue === '9') || (content.isOfficial==1 && ['5','6','7','8','9'].includes(item.dictValue))"
+                                                :disabled="(content.type!=2 && (item.dictValue === '9' || item.dictValue === '17')) || (content.isOfficial==1 && ['5','6','7','8','9','17'].includes(item.dictValue))"
                                                 v-for="item in sysQwSopAiContentType"
                                                 v-if="courseTypeList.includes(item.dictValue)">{{ item.dictLabel }}
                                               </el-radio>
@@ -395,7 +395,7 @@
                                                        type="image" :num="1" :width="150" :height="150"/>
 
                                           <div
-                                            v-if="(setList.contentType == 3 || item.contentType ==9) || (setList.contentType == 9 && content.type==2 )">
+                                            v-if="(setList.contentType == 3 || item.contentType ==9) || ((setList.contentType == 9 || setList.contentType == 17) && content.type==2 )">
                                             <el-card class="box-card">
                                               <el-form-item label="链接标题:" label-width="100px" required>
                                                 <el-input :disabled="formType == 3 || (form.sendType == 11 && contentIndex != 0 && setIndex == 0)" v-model="setList.linkTitle"
@@ -426,7 +426,8 @@
                                               </div>
                                               <div v-if="content.type == 2 ">
                                                 <el-form-item label="链接地址:" label-width="100px">
-                                                  <el-tag type="warning" v-model="setList.isBindUrl = 1 ">选择的课程小节
+                                                  <el-tag v-if="setList.contentType == 17" type="info">飞书看课链接由服务端根据所选课程、课节自动生成</el-tag>
+                                                  <el-tag v-else type="warning" v-model="setList.isBindUrl = 1 ">选择的课程小节
                                                     即为卡片链接地址
                                                   </el-tag>
                                                 </el-form-item>
@@ -951,6 +952,7 @@ import {listToLiveNoEnd} from "@/api/live/live";
 import ImageUpload from "@/views/qw/sop/ImageUpload";
 import userVideo from "@/views/qw/userVideo/userVideo.vue";
 import {listReward} from "@/api/qw/luckyBag";
+import { listFeishuAccountOptions } from '@/api/feishu/feishuAccount'
 import RandomTextListEditor from "@/components/RandomTextListEditor/index.vue";
 import {
   getRoles,
@@ -996,7 +998,7 @@ export default {
       ruleList: [],
       ids: [],
       // startTimeRange: [],
-      courseTypeList: ['1','2', '4','5','6', '7','8','9','10','11'],
+      courseTypeList: ['1','2', '4','5','6', '7','8','9','10','11','17'],
       sysFsSopWatchStatus: [],
       //消息内容类型 企微版
       sysQwSopContentType: [],
@@ -1068,6 +1070,7 @@ export default {
         ],
       },
       liveList: [],
+      feishuAccountOptions: [],
     };
   },
   computed: {
@@ -1077,6 +1080,7 @@ export default {
     }
   },
   created() {
+    this.loadFeishuAccountOptions();
     getRoles().then(res => {
       this.roles = res.data;
     })
@@ -1120,6 +1124,11 @@ export default {
     this.handleUpdate(id);
   },
   methods: {
+    loadFeishuAccountOptions() {
+      listFeishuAccountOptions().then(res => {
+        this.feishuAccountOptions = res.data || []
+      })
+    },
     getLuckyBagStatus(content) {
       const selectedLuckyBag = this.luckyBagList.find(item => item.id === content.luckyBagId);
       if (selectedLuckyBag) {
@@ -1575,15 +1584,15 @@ export default {
                 this.$message.error("图片不能为空")
                 return false;
               }
-              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkTitle == null || data.content[j].setting[k].linkTitle == "")) {
+              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 17 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkTitle == null || data.content[j].setting[k].linkTitle == "")) {
                 this.$message.error("链接标题不能为空")
                 return false;
               }
-              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkDescribe == null || data.content[j].setting[k].linkDescribe == "")) {
+              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 17 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkDescribe == null || data.content[j].setting[k].linkDescribe == "")) {
                 this.$message.error("链接描述不能为空")
                 return false;
               }
-              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkImageUrl == null || data.content[j].setting[k].linkImageUrl == "")) {
+              if ((data.content[j].setting[k].contentType == 3 || data.content[j].setting[k].contentType == 9 || data.content[j].setting[k].contentType == 17 || data.content[j].setting[k].contentType == 10) && (data.content[j].setting[k].linkImageUrl == null || data.content[j].setting[k].linkImageUrl == "")) {
                 this.$message.error("链接图片不能为空")
                 return false;
               }
@@ -1654,15 +1663,15 @@ export default {
                 this.$message.error("图片不能为空")
                 return false;
               }
-              if ((set.contentType == 3 || set.contentType == 9) && (set.linkTitle == null || set.linkTitle == "")) {
+              if ((set.contentType == 3 || set.contentType == 9 || set.contentType == 17) && (set.linkTitle == null || set.linkTitle == "")) {
                 this.$message.error("链接标题不能为空")
                 return false;
               }
-              if ((set.contentType == 3 || set.contentType == 9) && (set.linkDescribe == null || set.linkDescribe == "")) {
+              if ((set.contentType == 3 || set.contentType == 9 || set.contentType == 17) && (set.linkDescribe == null || set.linkDescribe == "")) {
                 this.$message.error("链接描述不能为空")
                 return false;
               }
-              if ((set.contentType == 3 || set.contentType == 9) && (set.linkImageUrl == null || set.linkImageUrl == "")) {
+              if ((set.contentType == 3 || set.contentType == 9 || set.contentType == 17) && (set.linkImageUrl == null || set.linkImageUrl == "")) {
                 this.$message.error("链接图片不能为空")
                 return false;
               }
@@ -1932,7 +1941,7 @@ export default {
         if (selectedCourse && content.type == 2 && content.courseId != null) {
           //响应式直接给链接的标题/封面上值
 
-          if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+          if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9 || content.setting[i].contentType == 17) {
             this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
             this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
           }
@@ -2162,7 +2171,7 @@ export default {
         for (let i = 0; i < content.setting.length; i++) {
           //响应式直接给链接的标题/封面上值
           if (selectedCourse && content.type == 2 && content.courseId != null) {
-            if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+            if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9 || content.setting[i].contentType == 17) {
               this.$set(content.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(content.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
             }
@@ -2183,7 +2192,7 @@ export default {
           //响应式直接给链接的描述上值
           if (selectedVideo && content.type == 2 && content.videoId != null) {
 
-            if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+            if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9 || content.setting[i].contentType == 17) {
               this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
               if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
@@ -2227,7 +2236,7 @@ export default {
         //如果是链接的才上
         if (selectedVideo && content.type == 2 && content.videoId != null) {
 
-          if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9) {
+          if (content.setting[i].contentType == 3 || content.setting[i].contentType == 9 || content.setting[i].contentType == 17) {
             this.$set(content.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
             if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {

+ 11 - 10
src/views/qw/sopUserLogsInfo/sendMsgOpenTool.vue

@@ -178,7 +178,7 @@
 
                     <ImageUpload v-if="item.contentType == 2 " v-model="item.imgUrl" type="image" :num="1"  :width="150" :height="150" />
 
-                    <div v-if="item.contentType == 3 || item.contentType ==9 ">
+                    <div v-if="item.contentType == 3 || item.contentType == 9 || item.contentType == 17">
                       <el-card class="box-card">
                         <el-form-item label="链接标题:"  label-width="100px">
                           <el-input v-model="item.linkTitle" placeholder="请输入链接标题" style="width: 90%;"/>
@@ -190,7 +190,8 @@
                           <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
                         </el-form-item>
                         <el-form-item label="链接地址:"  label-width="100px" >
-                          <el-tag type="warning" v-model="item.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
+                          <el-tag v-if="item.contentType == 17" type="info">飞书看课链接由服务端根据所选课程、课节自动生成</el-tag>
+                          <el-tag v-else type="warning" v-model="item.isBindUrl=1">选择的课程小节 即为卡片链接地址</el-tag>
                         </el-form-item>
                       </el-card>
                     </div>
@@ -620,7 +621,7 @@ export default {
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的标题/封面上值
           if (selectedCourse && this.msgForm.courseId != null) {
-            if ( this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
+            if ( this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ){
               this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
             }
@@ -647,7 +648,7 @@ export default {
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的描述上值
           if (selectedVideo && this.msgForm.videoId != null) {
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
               if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
@@ -933,7 +934,7 @@ export default {
           //响应式直接给链接的标题/封面上值
           if (selectedCourse  && this.msgForm.courseId != null) {
 
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17){
               this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
             }
@@ -955,7 +956,7 @@ export default {
           //响应式直接给链接的描述上值
           if (selectedVideo  && this.msgForm.videoId != null) {
 
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
               if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
@@ -1055,16 +1056,16 @@ export default {
               if (this.setting[i].contentType == 2 && (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")) {
                 return this.$message.error("图片不能为空")
               }
-              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9  ) && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
+              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17  ) && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
                 return this.$message.error("链接标题不能为空")
               }
-              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ) && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
+              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ) && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
                 return this.$message.error("链接描述不能为空")
               }
-              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ) && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
+              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ) && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
                 return this.$message.error("链接图片不能为空")
               }
-              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 )&& this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
+              if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 )&& this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
                 return this.$message.error("链接地址不能为空")
               }
 

+ 35 - 10
src/views/qw/sopUserLogsInfo/sendMsgSopOpenTool.vue

@@ -129,6 +129,22 @@
                         </el-form-item>
                       </el-card>
                     </div>
+                    <div v-if="isFeishuContentType(item.contentType)">
+                      <el-card class="box-card">
+                        <el-form-item label="消息标题" label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="飞书链接卡片标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="描述" label-width="100px">
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入描述" style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="封面" label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
+                        </el-form-item>
+                        <el-form-item label="链接说明" label-width="100px">
+                          <el-tag type="info">飞书看课链接由服务端根据上方所选课程、课节自动生成</el-tag>
+                        </el-form-item>
+                      </el-card>
+                    </div>
                     <div v-if="item.contentType == 4 || item.contentType == 10 ">
                       <el-card class="box-card">
                         <el-form-item label="标题" prop="miniprogramTitle">
@@ -628,6 +644,12 @@ export default {
       }
       return String(ct).trim() === '9';
     },
+    isFeishuContentType(ct) {
+      if (ct === undefined || ct === null || ct === '') {
+        return false;
+      }
+      return String(ct).trim() === '17';
+    },
     isLinkH5ContentType(ct) {
       if (ct === undefined || ct === null || ct === '') {
         return false;
@@ -648,6 +670,9 @@ export default {
             delete o.expiresDays;
           }
         }
+        if (this.isFeishuContentType(row.contentType)) {
+          o.contentType = '17';
+        }
         return o;
       });
     },
@@ -657,7 +682,7 @@ export default {
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的标题/封面上值
           if (selectedCourse && this.msgForm.courseId != null) {
-            if ( this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
+            if ( this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ){
               this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
             }
@@ -684,7 +709,7 @@ export default {
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的描述上值
           if (selectedVideo && this.msgForm.videoId != null) {
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
               if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
@@ -915,7 +940,7 @@ export default {
           //响应式直接给链接的标题/封面上值
           if (selectedCourse  && this.msgForm.courseId != null) {
 
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17){
               this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
 
@@ -939,7 +964,7 @@ export default {
           //响应式直接给链接的描述上值
           if (selectedVideo  && this.msgForm.videoId != null) {
 
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
               if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
@@ -1015,14 +1040,14 @@ export default {
               if (this.setting[i].contentType == 2 && (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")) {
                 return this.$message.error("图片不能为空")
               }
-              if ((this.isLinkH5ContentType(this.setting[i].contentType) || this.isAppContentType(this.setting[i].contentType)) && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
-                return this.$message.error(this.isAppContentType(this.setting[i].contentType) ? "APP 消息标题不能为空" : "链接标题不能为空")
+              if ((this.isLinkH5ContentType(this.setting[i].contentType) || this.isAppContentType(this.setting[i].contentType) || this.isFeishuContentType(this.setting[i].contentType)) && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
+                return this.$message.error(this.isAppContentType(this.setting[i].contentType) ? "APP 消息标题不能为空" : (this.isFeishuContentType(this.setting[i].contentType) ? "飞书消息标题不能为空" : "链接标题不能为空"))
               }
-              if ((this.isLinkH5ContentType(this.setting[i].contentType) || this.isAppContentType(this.setting[i].contentType)) && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
-                return this.$message.error(this.isAppContentType(this.setting[i].contentType) ? "APP 描述不能为空" : "链接描述不能为空")
+              if ((this.isLinkH5ContentType(this.setting[i].contentType) || this.isAppContentType(this.setting[i].contentType) || this.isFeishuContentType(this.setting[i].contentType)) && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
+                return this.$message.error(this.isAppContentType(this.setting[i].contentType) ? "APP 描述不能为空" : (this.isFeishuContentType(this.setting[i].contentType) ? "飞书描述不能为空" : "链接描述不能为空"))
               }
-              if ((this.isLinkH5ContentType(this.setting[i].contentType) || this.isAppContentType(this.setting[i].contentType)) && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
-                return this.$message.error(this.isAppContentType(this.setting[i].contentType) ? "APP 封面不能为空" : "链接图片不能为空")
+              if ((this.isLinkH5ContentType(this.setting[i].contentType) || this.isAppContentType(this.setting[i].contentType) || this.isFeishuContentType(this.setting[i].contentType)) && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
+                return this.$message.error(this.isAppContentType(this.setting[i].contentType) ? "APP 封面不能为空" : (this.isFeishuContentType(this.setting[i].contentType) ? "飞书封面不能为空" : "链接图片不能为空"))
               }
               if (this.isLinkH5ContentType(this.setting[i].contentType) && this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
                 return this.$message.error("链接地址不能为空")

+ 32 - 11
src/views/qw/sopUserLogsInfo/sopUserLogsInfoDetails.vue

@@ -390,6 +390,22 @@
                         </el-form-item>
                       </el-card>
                     </div>
+                    <div v-if="isFeishuContentType(item.contentType)">
+                      <el-card class="box-card">
+                        <el-form-item label="消息标题" label-width="100px">
+                          <el-input v-model="item.linkTitle" placeholder="飞书链接卡片标题" style="width: 90%;"/>
+                        </el-form-item>
+                        <el-form-item label="描述" label-width="100px">
+                          <el-input type="textarea" :rows="3" v-model="item.linkDescribe" placeholder="请输入描述" style="width: 90%;margin-top: 1%;"/>
+                        </el-form-item>
+                        <el-form-item label="封面" label-width="100px">
+                          <ImageUpload v-model="item.linkImageUrl" type="image" :num="1" :file-size="2" :width="150" :height="150" style="margin-top: 1%;" />
+                        </el-form-item>
+                        <el-form-item label="链接说明" label-width="100px">
+                          <el-tag type="info">飞书看课链接由服务端根据上方所选课程、课节自动生成</el-tag>
+                        </el-form-item>
+                      </el-card>
+                    </div>
                     <div v-if="item.contentType == 4">
                       <el-card class="box-card">
                         <el-form-item label="标题" prop="miniprogramTitle">
@@ -707,7 +723,6 @@ import PaginationMore from "../../../components/PaginationMore/index.vue";
 import RandomTextListEditor from "@/components/RandomTextListEditor/index.vue";
 import {listToLiveNoEnd} from "@/api/live/live";
 import {listReward} from "@/api/qw/luckyBag";
-
 export default {
   name: "sopUserLogsInfoDetails",
   components: {PaginationMore, userVideo, ImageUpload, RandomTextListEditor},
@@ -887,6 +902,12 @@ export default {
 
   },
   methods: {
+    isFeishuContentType(ct) {
+      if (ct === undefined || ct === null || ct === '') {
+        return false;
+      }
+      return String(ct).trim() === '17';
+    },
 
     // 搜索排除标签
     handleChangeExTags() {
@@ -1200,7 +1221,7 @@ export default {
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的标题/封面上值
           if (selectedCourse && this.msgForm.courseId != null) {
-            if ( this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
+            if ( this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ){
               this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
             }
@@ -1227,7 +1248,7 @@ export default {
         for (let i = 0; i < this.setting.length; i++) {
           //响应式直接给链接的描述上值
           if (selectedVideo && this.msgForm.videoId != null) {
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
               if (this.projectFrom == 'sxjz' && selectedVideo.dictImgUrl) {
@@ -1506,7 +1527,7 @@ export default {
           //响应式直接给链接的标题/封面上值
           if (selectedCourse  && this.msgForm.courseId != null) {
 
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17){
               this.$set(this.setting[i], 'linkTitle', selectedCourse.dictLabel);
               this.$set(this.setting[i], 'linkImageUrl', selectedCourse.dictImgUrl);
             }
@@ -1527,7 +1548,7 @@ export default {
           //响应式直接给链接的描述上值
           if (selectedVideo  && this.msgForm.videoId != null) {
 
-            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9){
+            if (this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17){
               this.$set(this.setting[i], 'linkDescribe', selectedVideo.dictLabel);
 
               if (this.msgForm.videoId != null) {
@@ -1740,14 +1761,14 @@ export default {
             if (this.setting[i].contentType == 2 && (this.setting[i].imgUrl == null || this.setting[i].imgUrl == "")) {
               return this.$message.error("图片不能为空")
             }
-            if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9  ) && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
-              return this.$message.error("链接标题不能为空")
+            if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ) && (this.setting[i].linkTitle == null || this.setting[i].linkTitle == "")) {
+              return this.$message.error(this.isFeishuContentType(this.setting[i].contentType) ? "飞书消息标题不能为空" : "链接标题不能为空")
             }
-            if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ) && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
-              return this.$message.error("链接描述不能为空")
+            if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ) && (this.setting[i].linkDescribe == null || this.setting[i].linkDescribe == "")) {
+              return this.$message.error(this.isFeishuContentType(this.setting[i].contentType) ? "飞书描述不能为空" : "链接描述不能为空")
             }
-            if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 ) && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
-              return this.$message.error("链接图片不能为空")
+            if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 || this.setting[i].contentType == 17 ) && (this.setting[i].linkImageUrl == null || this.setting[i].linkImageUrl == "")) {
+              return this.$message.error(this.isFeishuContentType(this.setting[i].contentType) ? "飞书封面不能为空" : "链接图片不能为空")
             }
             if ((this.setting[i].contentType == 3 || this.setting[i].contentType == 9 )&& this.setting[i].type == 1 && (this.setting[i].linkUrl == null || this.setting[i].linkUrl == "")) {
               return this.$message.error("链接地址不能为空")