feat(群级配置): 新增结构化表单模式降低JSON维护门槛
- 为群成员变更监控/welcome 配置新增标准表单编辑模式,覆盖欢迎文本与卡片关键字段 - 保留高级JSON模式,支持标准表单与JSON双模式切换 - 新增变量提示与实时预览,便于运营同学所见即所得配置文案 - 增加URL与必填项校验,保存前拦截常见配置错误 - 标准表单字段变更实时同步JSON文本,确保两种模式数据一致
This commit is contained in:
@@ -77,7 +77,49 @@
|
||||
<el-form-item label="启用">
|
||||
<el-switch v-model="form.enabled"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="JSON配置">
|
||||
<el-form-item label="编辑模式" v-if="isWelcomeTemplateForm">
|
||||
<el-radio-group v-model="editorMode">
|
||||
<el-radio-button label="simple">标准表单</el-radio-button>
|
||||
<el-radio-button label="advanced">高级JSON</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="isWelcomeTemplateForm && editorMode === 'simple'">
|
||||
<div class="form-tip">
|
||||
变量支持:<code>{nickname}</code> <code>{wxid}</code> <code>{group_id}</code> <code>{now}</code> <code>{head_url}</code>
|
||||
</div>
|
||||
<el-form-item label="文本欢迎">
|
||||
<el-switch v-model="simpleWelcome.welcome_text_enabled"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="文本模板">
|
||||
<el-input v-model="simpleWelcome.welcome_text_template" placeholder="👏欢迎 {nickname} 加入群聊!🎉"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="欢迎卡片">
|
||||
<el-switch v-model="simpleWelcome.welcome_card_enabled"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="卡片标题">
|
||||
<el-input v-model="simpleWelcome.card_title_template" placeholder="👏欢迎 {nickname} 加入群聊!🎉"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="卡片描述">
|
||||
<el-input v-model="simpleWelcome.card_desc_template" placeholder="⌚时间:{now}"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="卡片URL">
|
||||
<el-input v-model="simpleWelcome.card_url" placeholder="https://example.com/welcome?gid={group_id}"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="缩略图URL">
|
||||
<el-input v-model="simpleWelcome.card_thumb_url" placeholder="{head_url}"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="效果预览">
|
||||
<div class="preview-box">
|
||||
<p><strong>文本:</strong>{% raw %}{{ previewWelcomeText }}{% endraw %}</p>
|
||||
<p><strong>标题:</strong>{% raw %}{{ previewCardTitle }}{% endraw %}</p>
|
||||
<p><strong>描述:</strong>{% raw %}{{ previewCardDesc }}{% endraw %}</p>
|
||||
<p><strong>URL:</strong>{% raw %}{{ previewCardUrl }}{% endraw %}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="JSON配置" v-if="!isWelcomeTemplateForm || editorMode === 'advanced'">
|
||||
<el-input type="textarea" :rows="12" v-model="form.config_json_text"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -103,6 +145,17 @@ new Vue({
|
||||
filters: { group_id: '', plugin_name: '' },
|
||||
dialogVisible: false,
|
||||
editing: false,
|
||||
editorMode: 'simple',
|
||||
// 标准表单模型:用于“群成员变更监控 + welcome”的结构化配置编辑。
|
||||
simpleWelcome: {
|
||||
welcome_text_enabled: true,
|
||||
welcome_text_template: '👏欢迎 {nickname} 加入群聊!🎉',
|
||||
welcome_card_enabled: true,
|
||||
card_title_template: '👏欢迎 {nickname} 加入群聊!🎉',
|
||||
card_desc_template: '⌚时间:{now}',
|
||||
card_url: 'https://newsnow.busiyi.world/',
|
||||
card_thumb_url: '{head_url}'
|
||||
},
|
||||
form: {
|
||||
group_id: '',
|
||||
plugin_name: '',
|
||||
@@ -117,7 +170,115 @@ new Vue({
|
||||
this.loadPlugins()
|
||||
this.loadRows()
|
||||
},
|
||||
computed: {
|
||||
// 仅当命中当前已接入模板时启用标准表单,其它插件继续使用JSON高级模式。
|
||||
isWelcomeTemplateForm() {
|
||||
return this.form.plugin_name === '群成员变更监控' && this.form.config_key === 'welcome'
|
||||
},
|
||||
previewVariables() {
|
||||
return {
|
||||
nickname: '张三',
|
||||
wxid: 'wxid_demo_123',
|
||||
group_id: this.form.group_id || '123456@chatroom',
|
||||
now: '2026-04-20 12:00:00',
|
||||
head_url: 'https://example.com/avatar.png'
|
||||
}
|
||||
},
|
||||
previewWelcomeText() {
|
||||
return this.renderTemplate(this.simpleWelcome.welcome_text_template)
|
||||
},
|
||||
previewCardTitle() {
|
||||
return this.renderTemplate(this.simpleWelcome.card_title_template)
|
||||
},
|
||||
previewCardDesc() {
|
||||
return this.renderTemplate(this.simpleWelcome.card_desc_template)
|
||||
},
|
||||
previewCardUrl() {
|
||||
return this.renderTemplate(this.simpleWelcome.card_url)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 当插件/配置键切换到结构化模板时,自动从JSON回填表单,减少手工搬运。
|
||||
'form.plugin_name'() {
|
||||
this.onTemplateTypeChanged()
|
||||
},
|
||||
'form.config_key'() {
|
||||
this.onTemplateTypeChanged()
|
||||
},
|
||||
// 在标准表单模式下,任何字段变化都实时同步到JSON文本,保证两种模式数据一致。
|
||||
simpleWelcome: {
|
||||
handler() {
|
||||
if (this.isWelcomeTemplateForm && this.editorMode === 'simple') {
|
||||
this.form.config_json_text = JSON.stringify(this.simpleWelcome, null, 2)
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
renderTemplate(template) {
|
||||
let result = String(template || '')
|
||||
Object.entries(this.previewVariables).forEach(([k, v]) => {
|
||||
result = result.replace(new RegExp(`\\{${k}\\}`, 'g'), String(v || ''))
|
||||
})
|
||||
return result
|
||||
},
|
||||
applyWelcomeDefaults() {
|
||||
this.simpleWelcome = {
|
||||
welcome_text_enabled: true,
|
||||
welcome_text_template: '👏欢迎 {nickname} 加入群聊!🎉',
|
||||
welcome_card_enabled: true,
|
||||
card_title_template: '👏欢迎 {nickname} 加入群聊!🎉',
|
||||
card_desc_template: '⌚时间:{now}',
|
||||
card_url: 'https://newsnow.busiyi.world/',
|
||||
card_thumb_url: '{head_url}'
|
||||
}
|
||||
this.form.config_json_text = JSON.stringify(this.simpleWelcome, null, 2)
|
||||
},
|
||||
onTemplateTypeChanged() {
|
||||
if (!this.isWelcomeTemplateForm) {
|
||||
this.editorMode = 'advanced'
|
||||
return
|
||||
}
|
||||
this.editorMode = 'simple'
|
||||
let parsed = {}
|
||||
try {
|
||||
parsed = JSON.parse(this.form.config_json_text || '{}')
|
||||
} catch (e) {
|
||||
parsed = {}
|
||||
}
|
||||
this.simpleWelcome = {
|
||||
welcome_text_enabled: parsed.welcome_text_enabled !== undefined ? !!parsed.welcome_text_enabled : true,
|
||||
welcome_text_template: String(parsed.welcome_text_template || '👏欢迎 {nickname} 加入群聊!🎉'),
|
||||
welcome_card_enabled: parsed.welcome_card_enabled !== undefined ? !!parsed.welcome_card_enabled : true,
|
||||
card_title_template: String(parsed.card_title_template || '👏欢迎 {nickname} 加入群聊!🎉'),
|
||||
card_desc_template: String(parsed.card_desc_template || '⌚时间:{now}'),
|
||||
card_url: String(parsed.card_url || 'https://newsnow.busiyi.world/'),
|
||||
card_thumb_url: String(parsed.card_thumb_url || '{head_url}')
|
||||
}
|
||||
this.form.config_json_text = JSON.stringify(this.simpleWelcome, null, 2)
|
||||
},
|
||||
validateSimpleWelcome() {
|
||||
// 结构化模式下做强校验,避免无效URL或空模板入库。
|
||||
if (!this.simpleWelcome.welcome_text_template.trim()) {
|
||||
this.$message.error('文本模板不能为空')
|
||||
return false
|
||||
}
|
||||
if (!this.simpleWelcome.card_title_template.trim()) {
|
||||
this.$message.error('卡片标题不能为空')
|
||||
return false
|
||||
}
|
||||
if (!this.simpleWelcome.card_url.trim()) {
|
||||
this.$message.error('卡片URL不能为空')
|
||||
return false
|
||||
}
|
||||
const renderedUrl = this.renderTemplate(this.simpleWelcome.card_url)
|
||||
if (!/^https?:\/\//i.test(renderedUrl)) {
|
||||
this.$message.error('卡片URL必须是 http 或 https 开头')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
async loadGroups() {
|
||||
const resp = await axios.get('/contacts/api/groups')
|
||||
const groups = (resp.data && resp.data.data && resp.data.data.groups) || {}
|
||||
@@ -149,6 +310,7 @@ new Vue({
|
||||
enabled: true,
|
||||
config_json_text: '{}'
|
||||
}
|
||||
this.applyWelcomeDefaults()
|
||||
this.dialogVisible = true
|
||||
},
|
||||
openEdit(row) {
|
||||
@@ -160,12 +322,22 @@ new Vue({
|
||||
enabled: !!row.enabled,
|
||||
config_json_text: JSON.stringify(row.config_json || {}, null, 2)
|
||||
}
|
||||
this.onTemplateTypeChanged()
|
||||
this.dialogVisible = true
|
||||
},
|
||||
async saveForm() {
|
||||
let parsed = {}
|
||||
if (this.isWelcomeTemplateForm && this.editorMode === 'simple') {
|
||||
if (!this.validateSimpleWelcome()) {
|
||||
return
|
||||
}
|
||||
parsed = { ...this.simpleWelcome }
|
||||
this.form.config_json_text = JSON.stringify(parsed, null, 2)
|
||||
}
|
||||
try {
|
||||
parsed = JSON.parse(this.form.config_json_text || '{}')
|
||||
parsed = this.isWelcomeTemplateForm && this.editorMode === 'simple'
|
||||
? parsed
|
||||
: JSON.parse(this.form.config_json_text || '{}')
|
||||
} catch (e) {
|
||||
this.$message.error('JSON 配置格式错误')
|
||||
return
|
||||
@@ -219,5 +391,9 @@ new Vue({
|
||||
.page-hero-copy h1{font-size:30px;line-height:1.1;margin-bottom:10px;color:#0f172a}
|
||||
.page-hero-copy p{color:#64748b;font-size:14px}
|
||||
.detail-pre{white-space:pre-wrap;word-break:break-word;background:rgba(248,250,252,.85);border:1px solid rgba(148,163,184,.12);border-radius:14px;padding:10px;color:#334155;max-height:180px;overflow:auto}
|
||||
.form-tip{padding:10px 12px;border-radius:10px;background:#f8fbff;border:1px solid #d9e8f8;color:#4a6179;margin:0 0 12px 0}
|
||||
.form-tip code{display:inline-block;margin-right:6px;background:#eef6ff;border:1px solid #d2e6ff;color:#12539a;padding:1px 6px;border-radius:6px;font-size:12px}
|
||||
.preview-box{padding:12px;border:1px dashed #c7d8ea;background:#f8fbff;border-radius:10px;color:#3f5c77;line-height:1.7}
|
||||
.preview-box p{margin:0 0 4px 0}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user