|
|
@@ -0,0 +1,281 @@
|
|
|
+<template>
|
|
|
+ <div class="app-container">
|
|
|
+ <el-alert
|
|
|
+ title="以下配置对全站所有直播间生效。"
|
|
|
+ type="warning"
|
|
|
+ :closable="false"
|
|
|
+ show-icon
|
|
|
+ class="mb16"
|
|
|
+ />
|
|
|
+ <!-- App 端需监听 WebSocket cmd=liveCommentConfig,data 为 JSON(仅全局 config),与总后台无直接耦合 -->
|
|
|
+ <el-card shadow="never">
|
|
|
+ <el-form
|
|
|
+ ref="form"
|
|
|
+ v-loading="loading"
|
|
|
+ :model="form"
|
|
|
+ :rules="rules"
|
|
|
+ label-width="160px"
|
|
|
+ >
|
|
|
+ <el-divider content-position="left">飘屏</el-divider>
|
|
|
+ <el-form-item label="飘屏总开关" prop="floatEnabled">
|
|
|
+ <el-switch
|
|
|
+ v-model="form.floatEnabled"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="用户飘屏冷却" prop="floatCooldownSec">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.floatCooldownSec"
|
|
|
+ :min="0"
|
|
|
+ :precision="0"
|
|
|
+ controls-position="right"
|
|
|
+ placeholder="秒"
|
|
|
+ />
|
|
|
+ <span class="form-tip">同一用户连续飘屏的最小间隔(秒)</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="可飘屏角色" prop="floatRoleCodes">
|
|
|
+ <el-select
|
|
|
+ v-model="floatRoleList"
|
|
|
+ multiple
|
|
|
+ filterable
|
|
|
+ placeholder="请选择角色"
|
|
|
+ style="width: 420px"
|
|
|
+ :loading="rolesLoading"
|
|
|
+ @visible-change="ensureRolesLoaded"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="r in roleOptions"
|
|
|
+ :key="r"
|
|
|
+ :label="r"
|
|
|
+ :value="r"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ <span class="form-tip">选项来自企业角色;保存为英文逗号分隔的 role_name</span>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-divider content-position="left">评论置顶</el-divider>
|
|
|
+ <el-form-item label="单房间最大置顶数" prop="pinMaxPerRoom">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.pinMaxPerRoom"
|
|
|
+ :min="1"
|
|
|
+ :precision="0"
|
|
|
+ controls-position="right"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="可选置顶时长" prop="pinDurationOptions">
|
|
|
+ <el-select
|
|
|
+ v-model="pinDurationList"
|
|
|
+ multiple
|
|
|
+ placeholder="分钟;-1 表示永久"
|
|
|
+ style="width: 420px"
|
|
|
+ >
|
|
|
+ <el-option label="5 分钟" :value="5" />
|
|
|
+ <el-option label="10 分钟" :value="10" />
|
|
|
+ <el-option label="15 分钟" :value="15" />
|
|
|
+ <el-option label="30 分钟" :value="30" />
|
|
|
+ <el-option label="60 分钟" :value="60" />
|
|
|
+ <el-option label="永久 (-1)" :value="-1" />
|
|
|
+ </el-select>
|
|
|
+ <span class="form-tip">保存为逗号分隔数字,如 5,10,30,-1</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="可置顶角色" prop="pinRoleCodes">
|
|
|
+ <el-select
|
|
|
+ v-model="pinRoleList"
|
|
|
+ multiple
|
|
|
+ filterable
|
|
|
+ placeholder="请选择角色"
|
|
|
+ style="width: 420px"
|
|
|
+ :loading="rolesLoading"
|
|
|
+ @visible-change="ensureRolesLoaded"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="r in roleOptions"
|
|
|
+ :key="r"
|
|
|
+ :label="r"
|
|
|
+ :value="r"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-divider content-position="left">其他</el-divider>
|
|
|
+ <el-form-item label="备注" prop="remark">
|
|
|
+ <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="备注" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="最近更新人">
|
|
|
+ <span>{{ form.updateBy || '—' }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="最近更新时间">
|
|
|
+ <span>{{ form.updateTime ? parseTime(form.updateTime) : '—' }}</span>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ :loading="saving"
|
|
|
+ @click="submitForm"
|
|
|
+ v-hasPermi="['live:commentFeature:edit']"
|
|
|
+ >保 存</el-button>
|
|
|
+ <el-button @click="loadConfig">重 置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ getCommentFeatureConfig,
|
|
|
+ updateCommentFeatureConfig,
|
|
|
+ getCommentFeatureRoles
|
|
|
+} from '@/api/live/commentFeature'
|
|
|
+
|
|
|
+const splitCodes = (s) => {
|
|
|
+ if (s == null || String(s).trim() === '') return []
|
|
|
+ return String(s)
|
|
|
+ .split(',')
|
|
|
+ .map((x) => x.trim())
|
|
|
+ .filter(Boolean)
|
|
|
+}
|
|
|
+
|
|
|
+const joinCodes = (arr) => (Array.isArray(arr) ? arr.map((x) => String(x).trim()).filter(Boolean) : []).join(',')
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'LiveCommentGlobalConfig',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ loading: false,
|
|
|
+ saving: false,
|
|
|
+ /** 下拉候选项(GET /live/commentFeature/roles),并与已选值合并以便回显 */
|
|
|
+ roleOptions: [],
|
|
|
+ rolesLoaded: false,
|
|
|
+ rolesLoading: false,
|
|
|
+ floatRoleList: [],
|
|
|
+ pinRoleList: [],
|
|
|
+ pinDurationList: [],
|
|
|
+ form: {
|
|
|
+ configId: 1,
|
|
|
+ floatEnabled: 0,
|
|
|
+ floatCooldownSec: 0,
|
|
|
+ floatRoleCodes: '',
|
|
|
+ pinMaxPerRoom: 1,
|
|
|
+ pinDurationOptions: '',
|
|
|
+ pinRoleCodes: '',
|
|
|
+ remark: '',
|
|
|
+ updateBy: '',
|
|
|
+ updateTime: null
|
|
|
+ },
|
|
|
+ rules: {
|
|
|
+ floatCooldownSec: [{ required: true, message: '请输入冷却秒数', trigger: 'blur' }],
|
|
|
+ pinMaxPerRoom: [{ required: true, message: '请输入单房间最大置顶数', trigger: 'blur' }]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.loadConfig()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /** 将已选 role_name 合并进 options,避免接口未返回历史已选项时标签不显示 */
|
|
|
+ mergeSelectedIntoRoleOptions() {
|
|
|
+ const set = new Set(Array.isArray(this.roleOptions) ? this.roleOptions : [])
|
|
|
+ ;(this.floatRoleList || []).forEach((x) => {
|
|
|
+ const s = x != null ? String(x).trim() : ''
|
|
|
+ if (s) set.add(s)
|
|
|
+ })
|
|
|
+ ;(this.pinRoleList || []).forEach((x) => {
|
|
|
+ const s = x != null ? String(x).trim() : ''
|
|
|
+ if (s) set.add(s)
|
|
|
+ })
|
|
|
+ this.roleOptions = Array.from(set)
|
|
|
+ },
|
|
|
+ /** 首次展开下拉时拉取角色列表;空数据不报错 */
|
|
|
+ ensureRolesLoaded(visible) {
|
|
|
+ if (!visible) return
|
|
|
+ if (this.rolesLoaded) {
|
|
|
+ this.mergeSelectedIntoRoleOptions()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.rolesLoading = true
|
|
|
+ getCommentFeatureRoles()
|
|
|
+ .then((response) => {
|
|
|
+ const list = response && response.data
|
|
|
+ this.roleOptions = Array.isArray(list) ? list.slice() : []
|
|
|
+ this.rolesLoaded = true
|
|
|
+ this.mergeSelectedIntoRoleOptions()
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ this.roleOptions = []
|
|
|
+ this.rolesLoaded = true
|
|
|
+ this.mergeSelectedIntoRoleOptions()
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.rolesLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ loadConfig() {
|
|
|
+ this.loading = true
|
|
|
+ getCommentFeatureConfig()
|
|
|
+ .then((response) => {
|
|
|
+ const d = response.data || {}
|
|
|
+ this.form = {
|
|
|
+ configId: d.configId != null ? d.configId : 1,
|
|
|
+ floatEnabled: Number(d.floatEnabled) === 1 ? 1 : 0,
|
|
|
+ floatCooldownSec: d.floatCooldownSec != null ? Number(d.floatCooldownSec) : 0,
|
|
|
+ floatRoleCodes: d.floatRoleCodes || '',
|
|
|
+ pinMaxPerRoom: d.pinMaxPerRoom != null ? Number(d.pinMaxPerRoom) : 1,
|
|
|
+ pinDurationOptions: d.pinDurationOptions || '',
|
|
|
+ pinRoleCodes: d.pinRoleCodes || '',
|
|
|
+ remark: d.remark || '',
|
|
|
+ updateBy: d.updateBy || '',
|
|
|
+ updateTime: d.updateTime
|
|
|
+ }
|
|
|
+ this.floatRoleList = splitCodes(this.form.floatRoleCodes)
|
|
|
+ this.pinRoleList = splitCodes(this.form.pinRoleCodes)
|
|
|
+ this.pinDurationList = splitCodes(this.form.pinDurationOptions).map((x) => {
|
|
|
+ const n = Number(x)
|
|
|
+ return Number.isNaN(n) ? x : n
|
|
|
+ })
|
|
|
+ this.mergeSelectedIntoRoleOptions()
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.loading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ submitForm() {
|
|
|
+ this.$refs.form.validate((valid) => {
|
|
|
+ if (!valid) return
|
|
|
+ const pinDur = joinCodes(this.pinDurationList)
|
|
|
+ const payload = {
|
|
|
+ configId: this.form.configId,
|
|
|
+ floatEnabled: this.form.floatEnabled,
|
|
|
+ floatCooldownSec: this.form.floatCooldownSec,
|
|
|
+ floatRoleCodes: joinCodes(this.floatRoleList),
|
|
|
+ pinMaxPerRoom: this.form.pinMaxPerRoom,
|
|
|
+ pinDurationOptions: pinDur,
|
|
|
+ pinRoleCodes: joinCodes(this.pinRoleList),
|
|
|
+ remark: (this.form.remark || '').trim()
|
|
|
+ }
|
|
|
+ this.saving = true
|
|
|
+ updateCommentFeatureConfig(payload)
|
|
|
+ .then(() => {
|
|
|
+ this.msgSuccess('保存成功')
|
|
|
+ this.loadConfig()
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.saving = false
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.mb16 {
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+.form-tip {
|
|
|
+ margin-left: 12px;
|
|
|
+ color: #909399;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+</style>
|